// 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 { // Dumping template std::ostream &DumpExprWithType(std::ostream &o, const std::variant &u) { std::visit( [&](const auto &x) { using Ty = typename std::remove_reference_t::Result; x.Dump(o << '(' << Ty::Dump() << "::") << ')'; }, u); return o; } 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 &GenericExpr::Dump(std::ostream &o) const { return DumpExpr(o, u); } template std::ostream &Unary::Dump( std::ostream &o, const char *opr) const { return operand().Dump(o << opr) << ')'; } 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 &IntegerExpr::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 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 Max &m) { m.Dump(o, ",", "MAX("); }, [&](const Min &m) { m.Dump(o, ",", "MIN("); }, [&](const auto &convert) { DumpExprWithType(o, convert.operand().u); }}, u_); return o; } template std::ostream &RealExpr::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 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("); }, [&](const RealPart &z) { z.Dump(o, "REAL("); }, [&](const AIMAG &p) { p.Dump(o, "AIMAG("); }, [&](const auto &convert) { DumpExprWithType(o, convert.operand().u); }}, u_); return o; } template std::ostream &ComplexExpr::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 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 CMPLX &c) { c.Dump(o, ","); }}, u_); return o; } template std::ostream &CharacterExpr::Dump(std::ostream &o) const { std::visit(common::visitors{[&](const Scalar &s) { o << parser::QuoteCharacterLiteral(s); }, [&](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 &LogicalExpr::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 Not &n) { n.Dump(o, "(.NOT."); }, [&](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 SubscriptIntegerExpr CharacterExpr::LEN() const { return std::visit( common::visitors{[](const Scalar &c) { // std::string::size_type isn't convertible to uint64_t // on Darwin return SubscriptIntegerExpr{ static_cast(c.size())}; }, [](const Concat &c) { return c.left().LEN() + c.right().LEN(); }, [](const Max &c) { return SubscriptIntegerExpr{ SubscriptIntegerExpr::Max{c.left().LEN(), c.right().LEN()}}; }, [](const Min &c) { return SubscriptIntegerExpr{ SubscriptIntegerExpr::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 Unary::Fold(FoldingContext &context) -> std::optional { if (std::optional c{operand_->Fold(context)}) { return static_cast(this)->FoldScalar(context, *c); } return std::nullopt; } 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 IntegerExpr::ConvertInteger::FoldScalar(FoldingContext &context, const ScalarConstant &c) -> std::optional { return std::visit( [&](auto &x) -> std::optional { auto converted{Scalar::ConvertSigned(x)}; if (converted.overflow) { context.messages.Say("integer conversion overflowed"_en_US); return std::nullopt; } return {std::move(converted.value)}; }, c.u); } template auto IntegerExpr::ConvertReal::FoldScalar(FoldingContext &context, const ScalarConstant &c) -> std::optional { return std::visit( [&](auto &x) -> std::optional { auto converted{x.template ToInteger()}; if (converted.flags.test(RealFlag::Overflow)) { context.messages.Say("real->integer conversion overflowed"_en_US); return std::nullopt; } if (converted.flags.test(RealFlag::InvalidArgument)) { context.messages.Say( "real->integer conversion: invalid argument"_en_US); return std::nullopt; } return {std::move(converted.value)}; }, c.u); } template auto IntegerExpr::Negate::FoldScalar( FoldingContext &context, const Scalar &c) -> std::optional { auto negated{c.Negate()}; if (negated.overflow) { context.messages.Say("integer negation overflowed"_en_US); return std::nullopt; } return {std::move(negated.value)}; } template auto IntegerExpr::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 IntegerExpr::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 IntegerExpr::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 IntegerExpr::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 IntegerExpr::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 IntegerExpr::Max::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { if (a.CompareSigned(b) == Ordering::Greater) { return {a}; } return {b}; } template auto IntegerExpr::Min::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { if (a.CompareSigned(b) == Ordering::Less) { return {a}; } return {b}; } template auto IntegerExpr::Fold(FoldingContext &context) -> std::optional { return std::visit( [&](auto &x) -> std::optional { using Ty = typename std::decay::type; if constexpr (std::is_same_v) { return {x}; } if constexpr (evaluate::FoldableTrait) { auto c{x.Fold(context)}; if (c.has_value()) { u_ = *c; return c; } } return std::nullopt; }, u_); } template auto RealExpr::ConvertInteger::FoldScalar(FoldingContext &context, const ScalarConstant &c) -> std::optional { return std::visit( [&](auto &x) -> std::optional { auto converted{Scalar::FromInteger(x)}; RealFlagWarnings(context, converted.flags, "integer->real conversion"); return {std::move(converted.value)}; }, c.u); } template auto RealExpr::ConvertReal::FoldScalar(FoldingContext &context, const ScalarConstant &c) -> std::optional { return std::visit( [&](auto &x) -> std::optional { auto converted{Scalar::Convert(x)}; RealFlagWarnings(context, converted.flags, "real conversion"); return {std::move(converted.value)}; }, c.u); } template auto RealExpr::Negate::FoldScalar( FoldingContext &context, const Scalar &c) -> std::optional { return {c.Negate()}; } template auto RealExpr::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 RealExpr::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 RealExpr::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 RealExpr::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 RealExpr::Power::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { return std::nullopt; // TODO } template auto RealExpr::IntPower::FoldScalar(FoldingContext &context, const Scalar &a, const ScalarConstant &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 RealExpr::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 RealExpr::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 RealExpr::RealPart::FoldScalar( FoldingContext &context, const CplxScalar &z) -> std::optional { return {z.REAL()}; } template auto RealExpr::AIMAG::FoldScalar( FoldingContext &context, const CplxScalar &z) -> std::optional { return {z.AIMAG()}; } template auto RealExpr::Fold(FoldingContext &context) -> std::optional { return std::visit( [&](auto &x) -> std::optional { using Ty = typename std::decay::type; if constexpr (std::is_same_v) { return {x}; } if constexpr (evaluate::FoldableTrait) { auto c{x.Fold(context)}; if (c.has_value()) { if (context.flushDenormalsToZero) { *c = c->FlushDenormalToZero(); } u_ = *c; return c; } } return std::nullopt; }, u_); } template auto ComplexExpr::Negate::FoldScalar( FoldingContext &context, const Scalar &c) -> std::optional { return {c.Negate()}; } template auto ComplexExpr::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 ComplexExpr::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 ComplexExpr::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 ComplexExpr::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 ComplexExpr::Power::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { return std::nullopt; // TODO } template auto ComplexExpr::IntPower::FoldScalar(FoldingContext &context, const Scalar &a, const ScalarConstant &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 ComplexExpr::CMPLX::FoldScalar(FoldingContext &context, const PartScalar &a, const PartScalar &b) -> std::optional { return {Scalar{a, b}}; } template auto ComplexExpr::Fold(FoldingContext &context) -> std::optional { return std::visit( [&](auto &x) -> std::optional { using Ty = typename std::decay::type; if constexpr (std::is_same_v) { return {x}; } if constexpr (evaluate::FoldableTrait) { auto c{x.Fold(context)}; if (c.has_value()) { if (context.flushDenormalsToZero) { *c = c->FlushDenormalToZero(); } u_ = *c; return c; } } return std::nullopt; }, u_); } template auto CharacterExpr::Concat::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { if constexpr (KIND == 1) { return {a + b}; } return std::nullopt; } template auto CharacterExpr::Max::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { if (Compare(a, b) == Ordering::Less) { return {b}; } return {a}; } template auto CharacterExpr::Min::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { if (Compare(a, b) == Ordering::Greater) { return {b}; } return {a}; } template auto CharacterExpr::Fold(FoldingContext &context) -> std::optional { return std::visit( [&](auto &x) -> std::optional { using Ty = typename std::decay::type; if constexpr (std::is_same_v) { return {x}; } if constexpr (evaluate::FoldableTrait) { auto c{x.Fold(context)}; if (c.has_value()) { u_ = *c; return c; } } return std::nullopt; }, u_); } template auto Comparison::FoldScalar(FoldingContext &c, const OperandScalarConstant &a, const OperandScalarConstant &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 LogicalExpr::Not::FoldScalar( FoldingContext &context, const Scalar &x) -> std::optional { return {Scalar{!x.IsTrue()}}; } template auto LogicalExpr::And::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { return {Scalar{a.IsTrue() && b.IsTrue()}}; } template auto LogicalExpr::Or::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { return {Scalar{a.IsTrue() || b.IsTrue()}}; } template auto LogicalExpr::Eqv::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { return {Scalar{a.IsTrue() == b.IsTrue()}}; } template auto LogicalExpr::Neqv::FoldScalar(FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional { return {Scalar{a.IsTrue() != b.IsTrue()}}; } template auto LogicalExpr::Fold(FoldingContext &context) -> std::optional { return std::visit( [&](auto &x) -> std::optional { using Ty = typename std::decay::type; if constexpr (std::is_same_v) { return {x}; } if constexpr (evaluate::FoldableTrait) { std::optional c{x.Fold(context)}; if (c.has_value()) { u_ = *c; return c; } } return std::nullopt; }, u_); } std::optional GenericExpr::ScalarValue() const { return std::visit( common::visitors{ [](const BOZLiteralConstant &) -> std::optional { return std::nullopt; }, [](const auto &x) -> std::optional { if (auto c{x.ScalarValue()}) { return {GenericScalar{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); } std::optional GenericExpr::Fold(FoldingContext &context) { return std::visit( common::visitors{ [](BOZLiteralConstant &) -> std::optional { return std::nullopt; }, [&](auto &x) -> std::optional { if (auto c{x.Fold(context)}) { return {GenericScalar{std::move(*c)}}; } return std::nullopt; }}, 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>; 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 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>; } // namespace Fortran::evaluate