// 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 "tools.h" #include "../common/idioms.h" #include "../parser/message.h" #include #include using namespace Fortran::parser::literals; namespace Fortran::evaluate { using SameRealExprPair = SameKindExprs; static SameRealExprPair ConversionHelper( Expr &&x, Expr &&y) { return std::visit( [&](auto &&rx, auto &&ry) -> SameRealExprPair { using XTy = ResultType; using YTy = ResultType; if constexpr (std::is_same_v) { return {SameExprs{std::move(rx), std::move(ry)}}; } else if constexpr (XTy::kind < YTy::kind) { return {SameExprs{ConvertTo(ry, std::move(rx)), std::move(ry)}}; } else { return {SameExprs{std::move(rx), ConvertTo(rx, std::move(ry))}}; } }, std::move(x.u), std::move(y.u)); } ConvertRealOperandsResult ConvertRealOperands( parser::ContextualMessages &messages, Expr &&x, Expr &&y) { return std::visit( common::visitors{ [&](Expr &&ix, Expr &&iy) -> ConvertRealOperandsResult { // Can happen in a CMPLX() constructor. Per F'2018, // both integer operands are converted to default REAL. return {ConversionHelper(ConvertToType(std::move(ix)), ConvertToType(std::move(iy)))}; }, [&](Expr &&ix, Expr &&ry) -> ConvertRealOperandsResult { return { ConversionHelper(ConvertTo(ry, std::move(ix)), std::move(ry))}; }, [&](Expr &&rx, Expr &&iy) -> ConvertRealOperandsResult { return { ConversionHelper(std::move(rx), ConvertTo(rx, std::move(iy)))}; }, [&](Expr &&rx, Expr &&ry) -> ConvertRealOperandsResult { return {ConversionHelper(std::move(rx), std::move(ry))}; }, [&](auto &&, auto &&) -> ConvertRealOperandsResult { // TODO: allow BOZ here? messages.Say("operands must be INTEGER or REAL"_err_en_US); return std::nullopt; }}, std::move(x.u), std::move(y.u)); } ConvertRealOperandsResult ConvertRealOperands( parser::ContextualMessages &messages, std::optional> &&x, std::optional> &&y) { auto partial{[&](Expr &&x, Expr &&y) { return ConvertRealOperands(messages, std::move(x), std::move(y)); }}; using fType = ConvertRealOperandsResult(Expr &&, Expr &&); std::function f{partial}; return common::JoinOptional( common::MapOptional(std::move(f), std::move(x), std::move(y))); } template class OPR, TypeCategory CAT> std::optional> PromoteAndCombine( Expr> &&x, Expr> &&y) { return {Expr{std::visit( [&](auto &&xk, auto &&yk) -> Expr> { using xt = ResultType; using yt = ResultType; using ToType = Type; return {Expr{OPR{EnsureKind(std::move(xk)), EnsureKind(std::move(yk))}}}; }, std::move(x.u), std::move(y.u))}}; } template class OPR> std::optional> NumericOperation( parser::ContextualMessages &messages, Expr &&x, Expr &&y) { return std::visit( common::visitors{[](Expr &&ix, Expr &&iy) { return PromoteAndCombine( std::move(ix), std::move(iy)); }, [](Expr &&rx, Expr &&ry) { return PromoteAndCombine( std::move(rx), std::move(ry)); }, [](Expr &&rx, Expr &&iy) { return std::optional{Expr{std::visit( [&](auto &&rxk) -> Expr { using kindEx = decltype(rxk); using resultType = ResultType; return {kindEx{OPR{std::move(rxk), ConvertToType(std::move(iy))}}}; }, std::move(rx.u))}}; }, [](Expr &&ix, Expr &&ry) { return std::optional{Expr{std::visit( [&](auto &&ryk) -> Expr { using kindEx = decltype(ryk); using resultType = ResultType; return {kindEx{ OPR{ConvertToType(std::move(ix)), std::move(ryk)}}}; }, std::move(ry.u))}}; }, [](Expr &&zx, Expr &&zy) { return PromoteAndCombine( std::move(zx), std::move(zy)); }, // TODO pmk complex; Add/Sub different from Mult/Div [&](auto &&, auto &&) { messages.Say("non-numeric operands to numeric operation"_err_en_US); return std::optional>{std::nullopt}; }}, std::move(x.u), std::move(y.u)); } template std::optional> NumericOperation( parser::ContextualMessages &, Expr &&, Expr &&); } // namespace Fortran::evaluate