//===-- lib/Evaluate/tools.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "flang/Evaluate/tools.h" #include "flang/Common/idioms.h" #include "flang/Evaluate/characteristics.h" #include "flang/Evaluate/traverse.h" #include "flang/Parser/message.h" #include "flang/Semantics/tools.h" #include #include using namespace Fortran::parser::literals; namespace Fortran::evaluate { // Can x*(a,b) be represented as (x*a,x*b)? This code duplication // of the subexpression "x" cannot (yet?) be reliably undone by // common subexpression elimination in lowering, so it's disabled // here for now to avoid the risk of potential duplication of // expensive subexpressions (e.g., large array expressions, references // to expensive functions) in generate code. static constexpr bool allowOperandDuplication{false}; std::optional> AsGenericExpr(DataRef &&ref) { const Symbol &symbol{ref.GetLastSymbol()}; if (auto dyType{DynamicType::From(symbol)}) { return TypedWrapper(*dyType, std::move(ref)); } return std::nullopt; } std::optional> AsGenericExpr(const Symbol &symbol) { return AsGenericExpr(DataRef{symbol}); } Expr Parenthesize(Expr &&expr) { return std::visit( [&](auto &&x) { using T = std::decay_t; if constexpr (common::HasMember) { return expr; // no parentheses around typeless } else if constexpr (std::is_same_v>) { return AsGenericExpr(Parentheses{std::move(x)}); } else { return std::visit( [](auto &&y) { using T = ResultType; return AsGenericExpr(Parentheses{std::move(y)}); }, std::move(x.u)); } }, std::move(expr.u)); } std::optional ExtractDataRef( const ActualArgument &arg, bool intoSubstring) { if (const Expr *expr{arg.UnwrapExpr()}) { return ExtractDataRef(*expr, intoSubstring); } else { return std::nullopt; } } std::optional ExtractSubstringBase(const Substring &substring) { return std::visit( common::visitors{ [&](const DataRef &x) -> std::optional { return x; }, [&](const StaticDataObject::Pointer &) -> std::optional { return std::nullopt; }, }, substring.parent()); } // IsVariable() auto IsVariableHelper::operator()(const Symbol &symbol) const -> Result { const Symbol &root{GetAssociationRoot(symbol)}; return !IsNamedConstant(root) && root.has(); } auto IsVariableHelper::operator()(const Component &x) const -> Result { const Symbol &comp{x.GetLastSymbol()}; return (*this)(comp) && (IsPointer(comp) || (*this)(x.base())); } auto IsVariableHelper::operator()(const ArrayRef &x) const -> Result { return (*this)(x.base()); } auto IsVariableHelper::operator()(const Substring &x) const -> Result { return (*this)(x.GetBaseObject()); } auto IsVariableHelper::operator()(const ProcedureDesignator &x) const -> Result { if (const Symbol * symbol{x.GetSymbol()}) { const Symbol *result{FindFunctionResult(*symbol)}; return result && IsPointer(*result) && !IsProcedurePointer(*result); } return false; } // Conversions of COMPLEX component expressions to REAL. ConvertRealOperandsResult ConvertRealOperands( parser::ContextualMessages &messages, Expr &&x, Expr &&y, int defaultRealKind) { 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 {AsSameKindExprs( ConvertToKind( defaultRealKind, std::move(ix)), ConvertToKind( defaultRealKind, std::move(iy)))}; }, [&](Expr &&ix, Expr &&ry) -> ConvertRealOperandsResult { return {AsSameKindExprs( ConvertTo(ry, std::move(ix)), std::move(ry))}; }, [&](Expr &&rx, Expr &&iy) -> ConvertRealOperandsResult { return {AsSameKindExprs( std::move(rx), ConvertTo(rx, std::move(iy)))}; }, [&](Expr &&rx, Expr &&ry) -> ConvertRealOperandsResult { return {AsSameKindExprs( std::move(rx), std::move(ry))}; }, [&](Expr &&ix, BOZLiteralConstant &&by) -> ConvertRealOperandsResult { return {AsSameKindExprs( ConvertToKind( defaultRealKind, std::move(ix)), ConvertToKind( defaultRealKind, std::move(by)))}; }, [&](BOZLiteralConstant &&bx, Expr &&iy) -> ConvertRealOperandsResult { return {AsSameKindExprs( ConvertToKind( defaultRealKind, std::move(bx)), ConvertToKind( defaultRealKind, std::move(iy)))}; }, [&](Expr &&rx, BOZLiteralConstant &&by) -> ConvertRealOperandsResult { return {AsSameKindExprs( std::move(rx), ConvertTo(rx, std::move(by)))}; }, [&](BOZLiteralConstant &&bx, Expr &&ry) -> ConvertRealOperandsResult { return {AsSameKindExprs( ConvertTo(ry, std::move(bx)), std::move(ry))}; }, [&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718 messages.Say("operands must be INTEGER or REAL"_err_en_US); return std::nullopt; }, }, std::move(x.u), std::move(y.u)); } // Helpers for NumericOperation and its subroutines below. static std::optional> NoExpr() { return std::nullopt; } template std::optional> Package(Expr> &&catExpr) { return {AsGenericExpr(std::move(catExpr))}; } template std::optional> Package( std::optional>> &&catExpr) { if (catExpr) { return {AsGenericExpr(std::move(*catExpr))}; } return NoExpr(); } // Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that // does not require conversion of the exponent expression. template