2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Evaluate/tools.cpp --------------------------------------------===//
|
2018-08-11 02:44:43 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// 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
|
2018-08-11 02:44:43 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-08-11 02:44:43 +08:00
|
|
|
|
2020-02-25 23:11:52 +08:00
|
|
|
#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"
|
2020-03-06 09:55:51 +08:00
|
|
|
#include "flang/Semantics/tools.h"
|
2018-08-24 01:55:16 +08:00
|
|
|
#include <algorithm>
|
2018-08-11 02:44:43 +08:00
|
|
|
#include <variant>
|
|
|
|
|
|
|
|
using namespace Fortran::parser::literals;
|
|
|
|
|
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
2019-11-23 06:58:26 +08:00
|
|
|
Expr<SomeType> Parenthesize(Expr<SomeType> &&expr) {
|
|
|
|
return std::visit(
|
|
|
|
[&](auto &&x) {
|
|
|
|
using T = std::decay_t<decltype(x)>;
|
|
|
|
if constexpr (common::HasMember<T, TypelessExpression> ||
|
|
|
|
std::is_same_v<T, Expr<SomeDerived>>) {
|
2020-03-28 05:17:25 +08:00
|
|
|
return expr; // no parentheses around typeless or derived type
|
2019-11-23 06:58:26 +08:00
|
|
|
} else {
|
|
|
|
return std::visit(
|
|
|
|
[](auto &&y) {
|
|
|
|
using T = ResultType<decltype(y)>;
|
|
|
|
return AsGenericExpr(Parentheses<T>{std::move(y)});
|
|
|
|
},
|
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
std::move(expr.u));
|
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
std::optional<DataRef> ExtractSubstringBase(const Substring &substring) {
|
2020-03-06 09:55:51 +08:00
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[&](const DataRef &x) -> std::optional<DataRef> { return x; },
|
|
|
|
[&](const StaticDataObject::Pointer &) -> std::optional<DataRef> {
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
substring.parent());
|
|
|
|
}
|
|
|
|
|
2019-07-24 01:55:56 +08:00
|
|
|
// IsVariable()
|
2020-03-06 09:55:51 +08:00
|
|
|
|
|
|
|
auto IsVariableHelper::operator()(const Symbol &symbol) const -> Result {
|
|
|
|
return !symbol.attrs().test(semantics::Attr::PARAMETER);
|
|
|
|
}
|
|
|
|
auto IsVariableHelper::operator()(const Component &x) const -> Result {
|
|
|
|
return (*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());
|
|
|
|
}
|
2019-09-20 05:56:12 +08:00
|
|
|
auto IsVariableHelper::operator()(const ProcedureDesignator &x) const
|
|
|
|
-> Result {
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol *symbol{x.GetSymbol()};
|
2019-09-20 05:56:12 +08:00
|
|
|
return symbol && symbol->attrs().test(semantics::Attr::POINTER);
|
2019-07-24 01:55:56 +08:00
|
|
|
}
|
|
|
|
|
2020-11-14 01:40:59 +08:00
|
|
|
// Conversions of COMPLEX component expressions to REAL.
|
2018-08-24 01:55:16 +08:00
|
|
|
ConvertRealOperandsResult ConvertRealOperands(
|
2018-08-21 00:29:08 +08:00
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&x,
|
2018-09-19 02:29:01 +08:00
|
|
|
Expr<SomeType> &&y, int defaultRealKind) {
|
2018-08-11 02:44:43 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](Expr<SomeInteger> &&ix,
|
|
|
|
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
// Can happen in a CMPLX() constructor. Per F'2018,
|
|
|
|
// both integer operands are converted to default REAL.
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
|
|
|
},
|
2018-08-29 06:15:18 +08:00
|
|
|
[&](Expr<SomeInteger> &&ix,
|
|
|
|
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
|
2018-09-01 07:14:14 +08:00
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertTo(ry, std::move(ix)), std::move(ry))};
|
2018-08-11 02:44:43 +08:00
|
|
|
},
|
2018-08-29 06:15:18 +08:00
|
|
|
[&](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
|
2018-09-01 07:14:14 +08:00
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
std::move(rx), ConvertTo(rx, std::move(iy)))};
|
2018-08-11 02:44:43 +08:00
|
|
|
},
|
2018-08-29 06:15:18 +08:00
|
|
|
[&](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
|
2018-09-01 07:14:14 +08:00
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
std::move(rx), std::move(ry))};
|
2018-08-11 02:44:43 +08:00
|
|
|
},
|
2018-09-08 06:25:10 +08:00
|
|
|
[&](Expr<SomeInteger> &&ix,
|
|
|
|
BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
2018-09-19 02:29:01 +08:00
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(by)))};
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](BOZLiteralConstant &&bx,
|
|
|
|
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
2018-09-19 02:29:01 +08:00
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(bx)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx,
|
|
|
|
BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
std::move(rx), ConvertTo(rx, std::move(by)))};
|
|
|
|
},
|
|
|
|
[&](BOZLiteralConstant &&bx,
|
|
|
|
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertTo(ry, std::move(bx)), std::move(ry))};
|
|
|
|
},
|
2020-03-28 05:17:25 +08:00
|
|
|
[&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718
|
2018-08-11 02:44:43 +08:00
|
|
|
messages.Say("operands must be INTEGER or REAL"_err_en_US);
|
|
|
|
return std::nullopt;
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-08-11 02:44:43 +08:00
|
|
|
std::move(x.u), std::move(y.u));
|
|
|
|
}
|
|
|
|
|
2018-09-08 01:33:32 +08:00
|
|
|
// Helpers for NumericOperation and its subroutines below.
|
|
|
|
static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <TypeCategory CAT>
|
2018-08-31 01:09:44 +08:00
|
|
|
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
|
|
|
|
return {AsGenericExpr(std::move(catExpr))};
|
2018-08-24 01:55:16 +08:00
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
template <TypeCategory CAT>
|
2018-09-05 07:42:32 +08:00
|
|
|
std::optional<Expr<SomeType>> Package(
|
|
|
|
std::optional<Expr<SomeKind<CAT>>> &&catExpr) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (catExpr) {
|
2018-09-05 07:42:32 +08:00
|
|
|
return {AsGenericExpr(std::move(*catExpr))};
|
|
|
|
}
|
2018-09-08 01:33:32 +08:00
|
|
|
return NoExpr();
|
2018-09-05 07:42:32 +08:00
|
|
|
}
|
|
|
|
|
2018-09-08 06:25:10 +08:00
|
|
|
// Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that
|
|
|
|
// does not require conversion of the exponent expression.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <template <typename> class OPR>
|
2018-09-08 06:25:10 +08:00
|
|
|
std::optional<Expr<SomeType>> MixedRealLeft(
|
|
|
|
Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
|
|
|
return Package(std::visit(
|
|
|
|
[&](auto &&rxk) -> Expr<SomeReal> {
|
|
|
|
using resultType = ResultType<decltype(rxk)>;
|
|
|
|
if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
|
2018-09-18 02:31:38 +08:00
|
|
|
return AsCategoryExpr(
|
|
|
|
RealToIntPower<resultType>{std::move(rxk), std::move(iy)});
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
// G++ 8.1.0 emits bogus warnings about missing return statements if
|
|
|
|
// this statement is wrapped in an "else", as it should be.
|
2018-09-18 02:31:38 +08:00
|
|
|
return AsCategoryExpr(OPR<resultType>{
|
|
|
|
std::move(rxk), ConvertToType<resultType>(std::move(iy))});
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
std::move(rx.u)));
|
|
|
|
}
|
|
|
|
|
2018-09-05 07:42:32 +08:00
|
|
|
std::optional<Expr<SomeComplex>> ConstructComplex(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&real,
|
2018-09-19 02:29:01 +08:00
|
|
|
Expr<SomeType> &&imaginary, int defaultRealKind) {
|
2018-09-05 07:42:32 +08:00
|
|
|
if (auto converted{ConvertRealOperands(
|
2018-09-19 02:29:01 +08:00
|
|
|
messages, std::move(real), std::move(imaginary), defaultRealKind)}) {
|
2018-09-05 07:42:32 +08:00
|
|
|
return {std::visit(
|
|
|
|
[](auto &&pair) {
|
|
|
|
return MakeComplex(std::move(pair[0]), std::move(pair[1]));
|
|
|
|
},
|
|
|
|
std::move(*converted))};
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeComplex>> ConstructComplex(
|
|
|
|
parser::ContextualMessages &messages, std::optional<Expr<SomeType>> &&real,
|
2018-09-19 02:29:01 +08:00
|
|
|
std::optional<Expr<SomeType>> &&imaginary, int defaultRealKind) {
|
2018-09-05 07:42:32 +08:00
|
|
|
if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) {
|
2019-06-19 06:15:22 +08:00
|
|
|
return ConstructComplex(messages, std::get<0>(std::move(*parts)),
|
|
|
|
std::get<1>(std::move(*parts)), defaultRealKind);
|
2018-09-05 07:42:32 +08:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
Expr<SomeReal> GetComplexPart(const Expr<SomeComplex> &z, bool isImaginary) {
|
|
|
|
return std::visit(
|
|
|
|
[&](const auto &zk) {
|
|
|
|
static constexpr int kind{ResultType<decltype(zk)>::kind};
|
2018-09-18 02:31:38 +08:00
|
|
|
return AsCategoryExpr(ComplexComponent<kind>{isImaginary, zk});
|
2018-09-05 07:42:32 +08:00
|
|
|
},
|
|
|
|
z.u);
|
|
|
|
}
|
|
|
|
|
2019-12-06 17:18:20 +08:00
|
|
|
// Convert REAL to COMPLEX of the same kind. Preserving the real operand kind
|
|
|
|
// and then applying complex operand promotion rules allows the result to have
|
|
|
|
// the highest precision of REAL and COMPLEX operands as required by Fortran
|
|
|
|
// 2018 10.9.1.3.
|
|
|
|
Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) {
|
|
|
|
return std::visit(
|
|
|
|
[](auto &&x) {
|
|
|
|
using RT = ResultType<decltype(x)>;
|
|
|
|
return AsCategoryExpr(ComplexConstructor<RT::kind>{
|
|
|
|
std::move(x), AsExpr(Constant<RT>{Scalar<RT>{}})});
|
|
|
|
},
|
|
|
|
std::move(someX.u));
|
|
|
|
}
|
|
|
|
|
2018-09-08 06:25:10 +08:00
|
|
|
// Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way
|
2018-09-06 08:12:03 +08:00
|
|
|
// than just converting the second operand to COMPLEX and performing the
|
|
|
|
// corresponding COMPLEX+COMPLEX operation.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <template <typename> class OPR, TypeCategory RCAT>
|
2018-09-06 08:12:03 +08:00
|
|
|
std::optional<Expr<SomeType>> MixedComplexLeft(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeComplex> &&zx,
|
2018-10-16 08:11:24 +08:00
|
|
|
Expr<SomeKind<RCAT>> &&iry, int defaultRealKind) {
|
2018-09-05 07:42:32 +08:00
|
|
|
Expr<SomeReal> zr{GetComplexPart(zx, false)};
|
|
|
|
Expr<SomeReal> zi{GetComplexPart(zx, true)};
|
2018-10-16 08:11:24 +08:00
|
|
|
if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>> ||
|
|
|
|
std::is_same_v<OPR<LargestReal>, Subtract<LargestReal>>) {
|
2018-09-06 08:12:03 +08:00
|
|
|
// (a,b) + x -> (a+x, b)
|
|
|
|
// (a,b) - x -> (a-x, b)
|
2018-10-16 08:11:24 +08:00
|
|
|
if (std::optional<Expr<SomeType>> rr{
|
|
|
|
NumericOperation<OPR>(messages, AsGenericExpr(std::move(zr)),
|
|
|
|
AsGenericExpr(std::move(iry)), defaultRealKind)}) {
|
|
|
|
return Package(ConstructComplex(messages, std::move(*rr),
|
|
|
|
AsGenericExpr(std::move(zi)), defaultRealKind));
|
2018-09-05 07:42:32 +08:00
|
|
|
}
|
2018-10-16 08:11:24 +08:00
|
|
|
} else if constexpr (std::is_same_v<OPR<LargestReal>,
|
|
|
|
Multiply<LargestReal>> ||
|
|
|
|
std::is_same_v<OPR<LargestReal>, Divide<LargestReal>>) {
|
2018-09-06 08:12:03 +08:00
|
|
|
// (a,b) * x -> (a*x, b*x)
|
|
|
|
// (a,b) / x -> (a/x, b/x)
|
2018-09-05 07:42:32 +08:00
|
|
|
auto copy{iry};
|
2020-08-13 07:35:26 +08:00
|
|
|
auto rr{NumericOperation<OPR>(messages, AsGenericExpr(std::move(zr)),
|
2018-10-16 08:11:24 +08:00
|
|
|
AsGenericExpr(std::move(iry)), defaultRealKind)};
|
2020-08-13 07:35:26 +08:00
|
|
|
auto ri{NumericOperation<OPR>(messages, AsGenericExpr(std::move(zi)),
|
2018-10-16 08:11:24 +08:00
|
|
|
AsGenericExpr(std::move(copy)), defaultRealKind)};
|
2018-09-05 07:42:32 +08:00
|
|
|
if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
|
2019-06-19 06:15:22 +08:00
|
|
|
return Package(ConstructComplex(messages, std::get<0>(std::move(*parts)),
|
|
|
|
std::get<1>(std::move(*parts)), defaultRealKind));
|
2018-09-05 07:42:32 +08:00
|
|
|
}
|
2018-09-08 06:25:10 +08:00
|
|
|
} else if constexpr (RCAT == TypeCategory::Integer &&
|
2018-10-16 08:11:24 +08:00
|
|
|
std::is_same_v<OPR<LargestReal>, Power<LargestReal>>) {
|
2018-09-08 06:25:10 +08:00
|
|
|
// COMPLEX**INTEGER is a special case that doesn't convert the exponent.
|
|
|
|
static_assert(RCAT == TypeCategory::Integer);
|
|
|
|
return Package(std::visit(
|
|
|
|
[&](auto &&zxk) {
|
|
|
|
using Ty = ResultType<decltype(zxk)>;
|
|
|
|
return AsCategoryExpr(
|
|
|
|
AsExpr(RealToIntPower<Ty>{std::move(zxk), std::move(iry)}));
|
|
|
|
},
|
|
|
|
std::move(zx.u)));
|
2020-03-28 05:17:25 +08:00
|
|
|
} else if (defaultRealKind != 666) { // dodge unused parameter warning
|
2018-09-08 06:25:10 +08:00
|
|
|
// (a,b) ** x -> (a,b) ** (x,0)
|
2019-12-06 17:18:20 +08:00
|
|
|
if constexpr (RCAT == TypeCategory::Integer) {
|
|
|
|
Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
|
|
|
|
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
|
|
|
|
} else {
|
|
|
|
Expr<SomeComplex> zy{PromoteRealToComplex(std::move(iry))};
|
|
|
|
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
|
|
|
|
}
|
2018-09-06 08:12:03 +08:00
|
|
|
}
|
2018-09-08 01:33:32 +08:00
|
|
|
return NoExpr();
|
2018-09-06 08:12:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mixed COMPLEX operations with the COMPLEX operand on the right.
|
|
|
|
// x + (a,b) -> (x+a, b)
|
|
|
|
// x - (a,b) -> (x-a, -b)
|
|
|
|
// x * (a,b) -> (x*a, x*b)
|
2018-09-08 06:25:10 +08:00
|
|
|
// x / (a,b) -> (x,0) / (a,b) (and **)
|
2020-03-28 05:17:25 +08:00
|
|
|
template <template <typename> class OPR, TypeCategory LCAT>
|
2018-09-06 08:12:03 +08:00
|
|
|
std::optional<Expr<SomeType>> MixedComplexRight(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
|
2018-10-16 08:11:24 +08:00
|
|
|
Expr<SomeComplex> &&zy, int defaultRealKind) {
|
|
|
|
if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>> ||
|
|
|
|
std::is_same_v<OPR<LargestReal>, Multiply<LargestReal>>) {
|
2018-09-06 08:12:03 +08:00
|
|
|
// x + (a,b) -> (a,b) + x -> (a+x, b)
|
|
|
|
// x * (a,b) -> (a,b) * x -> (a*x, b*x)
|
2020-08-13 07:35:26 +08:00
|
|
|
return MixedComplexLeft<OPR, LCAT>(
|
2018-10-16 08:11:24 +08:00
|
|
|
messages, std::move(zy), std::move(irx), defaultRealKind);
|
|
|
|
} else if constexpr (std::is_same_v<OPR<LargestReal>,
|
|
|
|
Subtract<LargestReal>>) {
|
2018-09-06 08:12:03 +08:00
|
|
|
// x - (a,b) -> (x-a, -b)
|
|
|
|
Expr<SomeReal> zr{GetComplexPart(zy, false)};
|
|
|
|
Expr<SomeReal> zi{GetComplexPart(zy, true)};
|
2018-10-16 08:11:24 +08:00
|
|
|
if (std::optional<Expr<SomeType>> rr{
|
|
|
|
NumericOperation<Subtract>(messages, AsGenericExpr(std::move(irx)),
|
|
|
|
AsGenericExpr(std::move(zr)), defaultRealKind)}) {
|
|
|
|
return Package(ConstructComplex(messages, std::move(*rr),
|
|
|
|
AsGenericExpr(-std::move(zi)), defaultRealKind));
|
2018-09-06 08:12:03 +08:00
|
|
|
}
|
2020-03-28 05:17:25 +08:00
|
|
|
} else if (defaultRealKind != 666) { // dodge unused parameter warning
|
2018-09-08 06:25:10 +08:00
|
|
|
// x / (a,b) -> (x,0) / (a,b)
|
2019-12-06 17:18:20 +08:00
|
|
|
if constexpr (LCAT == TypeCategory::Integer) {
|
2019-12-10 17:21:50 +08:00
|
|
|
Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
|
2019-12-06 17:18:20 +08:00
|
|
|
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
|
|
|
|
} else {
|
|
|
|
Expr<SomeComplex> zx{PromoteRealToComplex(std::move(irx))};
|
|
|
|
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
|
|
|
|
}
|
2018-09-05 07:42:32 +08:00
|
|
|
}
|
2018-09-08 01:33:32 +08:00
|
|
|
return NoExpr();
|
2018-09-05 07:42:32 +08:00
|
|
|
}
|
2018-08-24 01:55:16 +08:00
|
|
|
|
2018-09-05 05:20:48 +08:00
|
|
|
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
|
2018-09-08 06:25:10 +08:00
|
|
|
// the operands to a dyadic operation where one is permitted, it assumes the
|
|
|
|
// type and kind of the other operand.
|
2020-03-28 05:17:25 +08:00
|
|
|
template <template <typename> class OPR>
|
2018-08-24 01:55:16 +08:00
|
|
|
std::optional<Expr<SomeType>> NumericOperation(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&x,
|
2018-10-16 08:11:24 +08:00
|
|
|
Expr<SomeType> &&y, int defaultRealKind) {
|
2018-08-24 01:55:16 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
|
|
|
[](Expr<SomeInteger> &&ix, Expr<SomeInteger> &&iy) {
|
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Integer>(
|
|
|
|
std::move(ix), std::move(iy)));
|
|
|
|
},
|
2018-08-24 01:55:16 +08:00
|
|
|
[](Expr<SomeReal> &&rx, Expr<SomeReal> &&ry) {
|
2018-08-31 01:09:44 +08:00
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Real>(
|
|
|
|
std::move(rx), std::move(ry)));
|
2018-08-24 01:55:16 +08:00
|
|
|
},
|
2018-09-08 06:25:10 +08:00
|
|
|
// Mixed REAL/INTEGER operations
|
2018-08-24 01:55:16 +08:00
|
|
|
[](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
2018-09-08 06:25:10 +08:00
|
|
|
return MixedRealLeft<OPR>(std::move(rx), std::move(iy));
|
2018-08-24 01:55:16 +08:00
|
|
|
},
|
|
|
|
[](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
|
2018-08-31 01:09:44 +08:00
|
|
|
return Package(std::visit(
|
2018-08-24 01:55:16 +08:00
|
|
|
[&](auto &&ryk) -> Expr<SomeReal> {
|
2018-08-31 01:09:44 +08:00
|
|
|
using resultType = ResultType<decltype(ryk)>;
|
2018-09-18 02:31:38 +08:00
|
|
|
return AsCategoryExpr(
|
2018-09-05 07:42:32 +08:00
|
|
|
OPR<resultType>{ConvertToType<resultType>(std::move(ix)),
|
2018-09-18 02:31:38 +08:00
|
|
|
std::move(ryk)});
|
2018-08-24 01:55:16 +08:00
|
|
|
},
|
2018-08-31 01:09:44 +08:00
|
|
|
std::move(ry.u)));
|
2018-08-24 01:55:16 +08:00
|
|
|
},
|
2018-09-08 06:25:10 +08:00
|
|
|
// Homogeneous and mixed COMPLEX operations
|
2018-08-24 01:55:16 +08:00
|
|
|
[](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
|
2018-08-31 01:09:44 +08:00
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
|
|
|
|
std::move(zx), std::move(zy)));
|
2018-08-24 01:55:16 +08:00
|
|
|
},
|
2019-11-03 00:56:46 +08:00
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
|
2018-09-06 08:12:03 +08:00
|
|
|
return MixedComplexLeft<OPR>(
|
2019-11-03 00:56:46 +08:00
|
|
|
messages, std::move(zx), std::move(iy), defaultRealKind);
|
2018-09-05 07:42:32 +08:00
|
|
|
},
|
2019-11-03 00:56:46 +08:00
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
|
2018-09-06 08:12:03 +08:00
|
|
|
return MixedComplexLeft<OPR>(
|
2019-11-03 00:56:46 +08:00
|
|
|
messages, std::move(zx), std::move(ry), defaultRealKind);
|
2018-09-06 08:12:03 +08:00
|
|
|
},
|
2019-11-03 00:56:46 +08:00
|
|
|
[&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
|
2018-09-06 08:12:03 +08:00
|
|
|
return MixedComplexRight<OPR>(
|
2019-11-03 00:56:46 +08:00
|
|
|
messages, std::move(ix), std::move(zy), defaultRealKind);
|
2018-09-06 08:12:03 +08:00
|
|
|
},
|
2019-11-03 00:56:46 +08:00
|
|
|
[&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
|
2018-09-06 08:12:03 +08:00
|
|
|
return MixedComplexRight<OPR>(
|
2019-11-03 00:56:46 +08:00
|
|
|
messages, std::move(rx), std::move(zy), defaultRealKind);
|
2018-09-05 07:42:32 +08:00
|
|
|
},
|
2018-09-06 08:12:03 +08:00
|
|
|
// Operations with one typeless operand
|
2018-09-05 05:20:48 +08:00
|
|
|
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return NumericOperation<OPR>(messages,
|
2018-10-16 08:11:24 +08:00
|
|
|
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
|
|
|
|
defaultRealKind);
|
2018-09-01 07:14:14 +08:00
|
|
|
},
|
2018-09-05 05:20:48 +08:00
|
|
|
[&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return NumericOperation<OPR>(messages,
|
2018-10-16 08:11:24 +08:00
|
|
|
AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y),
|
|
|
|
defaultRealKind);
|
2018-09-01 07:14:14 +08:00
|
|
|
},
|
2018-09-05 05:20:48 +08:00
|
|
|
[&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return NumericOperation<OPR>(messages, std::move(x),
|
2018-10-16 08:11:24 +08:00
|
|
|
AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
|
2018-09-05 05:20:48 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return NumericOperation<OPR>(messages, std::move(x),
|
2018-10-16 08:11:24 +08:00
|
|
|
AsGenericExpr(ConvertTo(rx, std::move(by))), defaultRealKind);
|
2018-09-05 05:20:48 +08:00
|
|
|
},
|
2018-09-06 08:12:03 +08:00
|
|
|
// Default case
|
2018-08-24 01:55:16 +08:00
|
|
|
[&](auto &&, auto &&) {
|
2018-09-08 01:33:32 +08:00
|
|
|
// TODO: defined operator
|
2018-08-24 01:55:16 +08:00
|
|
|
messages.Say("non-numeric operands to numeric operation"_err_en_US);
|
2018-09-08 01:33:32 +08:00
|
|
|
return NoExpr();
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-08-24 01:55:16 +08:00
|
|
|
std::move(x.u), std::move(y.u));
|
|
|
|
}
|
|
|
|
|
2018-09-08 06:25:10 +08:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Power>(
|
2018-10-16 08:11:24 +08:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-09-01 07:14:14 +08:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
|
2018-10-16 08:11:24 +08:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-09-01 07:14:14 +08:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Divide>(
|
2018-10-16 08:11:24 +08:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-09-08 06:25:10 +08:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Add>(
|
2018-10-16 08:11:24 +08:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-09-08 06:25:10 +08:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
|
2018-10-16 08:11:24 +08:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-08-24 01:55:16 +08:00
|
|
|
|
2018-09-08 01:33:32 +08:00
|
|
|
std::optional<Expr<SomeType>> Negation(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&x) {
|
|
|
|
return std::visit(
|
2018-09-13 07:27:51 +08:00
|
|
|
common::visitors{
|
|
|
|
[&](BOZLiteralConstant &&) {
|
|
|
|
messages.Say("BOZ literal cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2019-02-20 09:06:28 +08:00
|
|
|
[&](NullPointer &&) {
|
|
|
|
messages.Say("NULL() cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2019-05-04 02:29:15 +08:00
|
|
|
[&](ProcedureDesignator &&) {
|
|
|
|
messages.Say("Subroutine cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
|
|
|
[&](ProcedureRef &&) {
|
|
|
|
messages.Say("Pointer to subroutine cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2018-12-06 05:11:55 +08:00
|
|
|
[&](Expr<SomeInteger> &&x) { return Package(-std::move(x)); },
|
2018-09-08 01:33:32 +08:00
|
|
|
[&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
|
|
|
|
[&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
|
2019-08-16 04:50:27 +08:00
|
|
|
[&](Expr<SomeCharacter> &&) {
|
2018-09-08 01:33:32 +08:00
|
|
|
// TODO: defined operator
|
|
|
|
messages.Say("CHARACTER cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2019-08-16 04:50:27 +08:00
|
|
|
[&](Expr<SomeLogical> &&) {
|
2018-09-08 01:33:32 +08:00
|
|
|
// TODO: defined operator
|
|
|
|
messages.Say("LOGICAL cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
2018-09-13 07:27:51 +08:00
|
|
|
},
|
2019-08-16 04:50:27 +08:00
|
|
|
[&](Expr<SomeDerived> &&) {
|
2018-09-13 07:27:51 +08:00
|
|
|
// TODO: defined operator
|
2019-05-14 00:33:18 +08:00
|
|
|
messages.Say("Operand cannot be negated"_err_en_US);
|
2018-09-13 07:27:51 +08:00
|
|
|
return NoExpr();
|
|
|
|
},
|
|
|
|
},
|
2018-09-08 01:33:32 +08:00
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
|
2018-09-08 07:54:33 +08:00
|
|
|
Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&x) {
|
|
|
|
return std::visit(
|
2019-01-08 02:15:27 +08:00
|
|
|
[](auto &&xk) { return AsCategoryExpr(LogicalNegation(std::move(xk))); },
|
2018-09-08 07:54:33 +08:00
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename T>
|
2018-09-08 06:25:10 +08:00
|
|
|
Expr<LogicalResult> PackageRelation(
|
|
|
|
RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
|
2018-11-30 02:25:46 +08:00
|
|
|
static_assert(IsSpecificIntrinsicType<T>);
|
2018-09-08 06:25:10 +08:00
|
|
|
return Expr<LogicalResult>{
|
|
|
|
Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
|
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <TypeCategory CAT>
|
2018-09-08 06:25:10 +08:00
|
|
|
Expr<LogicalResult> PromoteAndRelate(
|
|
|
|
RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
|
|
|
|
return std::visit(
|
|
|
|
[=](auto &&xy) {
|
|
|
|
return PackageRelation(opr, std::move(xy[0]), std::move(xy[1]));
|
|
|
|
},
|
|
|
|
AsSameKindExprs(std::move(x), std::move(y)));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
|
|
|
|
RelationalOperator opr, Expr<SomeType> &&x, Expr<SomeType> &&y) {
|
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
2019-06-04 01:51:51 +08:00
|
|
|
[=](Expr<SomeInteger> &&ix,
|
|
|
|
Expr<SomeInteger> &&iy) -> std::optional<Expr<LogicalResult>> {
|
|
|
|
return PromoteAndRelate(opr, std::move(ix), std::move(iy));
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
2019-06-04 01:51:51 +08:00
|
|
|
[=](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeReal> &&ry) -> std::optional<Expr<LogicalResult>> {
|
|
|
|
return PromoteAndRelate(opr, std::move(rx), std::move(ry));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return Relate(messages, opr, std::move(x),
|
|
|
|
AsGenericExpr(ConvertTo(rx, std::move(iy))));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return Relate(messages, opr,
|
|
|
|
AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
2019-06-04 01:51:51 +08:00
|
|
|
[&](Expr<SomeComplex> &&zx,
|
|
|
|
Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> {
|
2020-11-14 01:40:59 +08:00
|
|
|
if (opr == RelationalOperator::EQ ||
|
|
|
|
opr == RelationalOperator::NE) {
|
|
|
|
return PromoteAndRelate(opr, std::move(zx), std::move(zy));
|
|
|
|
} else {
|
2018-09-08 06:25:10 +08:00
|
|
|
messages.Say(
|
|
|
|
"COMPLEX data may be compared only for equality"_err_en_US);
|
2020-11-14 01:40:59 +08:00
|
|
|
return std::nullopt;
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return Relate(messages, opr, std::move(x),
|
|
|
|
AsGenericExpr(ConvertTo(zx, std::move(iy))));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return Relate(messages, opr, std::move(x),
|
|
|
|
AsGenericExpr(ConvertTo(zx, std::move(ry))));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return Relate(messages, opr,
|
|
|
|
AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
|
2018-09-13 07:27:51 +08:00
|
|
|
return Relate(messages, opr,
|
|
|
|
AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y));
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
[&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) {
|
|
|
|
return std::visit(
|
2019-06-04 01:51:51 +08:00
|
|
|
[&](auto &&cxk,
|
|
|
|
auto &&cyk) -> std::optional<Expr<LogicalResult>> {
|
2018-09-08 06:25:10 +08:00
|
|
|
using Ty = ResultType<decltype(cxk)>;
|
|
|
|
if constexpr (std::is_same_v<Ty, ResultType<decltype(cyk)>>) {
|
2019-06-04 01:51:51 +08:00
|
|
|
return PackageRelation(opr, std::move(cxk), std::move(cyk));
|
2018-09-08 06:25:10 +08:00
|
|
|
} else {
|
|
|
|
messages.Say(
|
|
|
|
"CHARACTER operands do not have same KIND"_err_en_US);
|
2019-08-02 02:41:05 +08:00
|
|
|
return std::nullopt;
|
2018-09-08 06:25:10 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
std::move(cx.u), std::move(cy.u));
|
|
|
|
},
|
|
|
|
// Default case
|
2019-08-02 02:41:05 +08:00
|
|
|
[&](auto &&, auto &&) {
|
2019-11-03 00:56:46 +08:00
|
|
|
DIE("invalid types for relational operator");
|
2019-08-02 02:41:05 +08:00
|
|
|
return std::optional<Expr<LogicalResult>>{};
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-09-08 06:25:10 +08:00
|
|
|
std::move(x.u), std::move(y.u));
|
|
|
|
}
|
|
|
|
|
|
|
|
Expr<SomeLogical> BinaryLogicalOperation(
|
|
|
|
LogicalOperator opr, Expr<SomeLogical> &&x, Expr<SomeLogical> &&y) {
|
2019-11-07 07:54:26 +08:00
|
|
|
CHECK(opr != LogicalOperator::Not);
|
2018-09-08 06:25:10 +08:00
|
|
|
return std::visit(
|
|
|
|
[=](auto &&xy) {
|
|
|
|
using Ty = ResultType<decltype(xy[0])>;
|
2019-01-08 02:15:27 +08:00
|
|
|
return Expr<SomeLogical>{BinaryLogicalOperation<Ty::kind>(
|
|
|
|
opr, std::move(xy[0]), std::move(xy[1]))};
|
2018-09-08 06:25:10 +08:00
|
|
|
},
|
|
|
|
AsSameKindExprs(std::move(x), std::move(y)));
|
|
|
|
}
|
2019-01-23 08:30:32 +08:00
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
template <TypeCategory TO>
|
2019-01-23 08:30:32 +08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
|
|
|
|
static_assert(common::IsNumericTypeCategory(TO));
|
|
|
|
return std::visit(
|
|
|
|
[=](auto &&cx) -> std::optional<Expr<SomeType>> {
|
|
|
|
using cxType = std::decay_t<decltype(cx)>;
|
2019-05-04 02:29:15 +08:00
|
|
|
if constexpr (!common::HasMember<cxType, TypelessExpression>) {
|
2019-01-23 08:30:32 +08:00
|
|
|
if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) {
|
2019-06-04 01:51:51 +08:00
|
|
|
return Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))};
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
|
|
|
const DynamicType &type, Expr<SomeType> &&x) {
|
2019-05-14 00:33:18 +08:00
|
|
|
switch (type.category()) {
|
2019-01-23 08:30:32 +08:00
|
|
|
case TypeCategory::Integer:
|
2019-06-04 01:51:51 +08:00
|
|
|
if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
|
|
|
|
// Extension to C7109: allow BOZ literals to appear in integer contexts
|
|
|
|
// when the type is unambiguous.
|
|
|
|
return Expr<SomeType>{
|
|
|
|
ConvertToKind<TypeCategory::Integer>(type.kind(), std::move(*boz))};
|
|
|
|
}
|
2019-05-14 00:33:18 +08:00
|
|
|
return ConvertToNumeric<TypeCategory::Integer>(type.kind(), std::move(x));
|
2019-01-23 08:30:32 +08:00
|
|
|
case TypeCategory::Real:
|
2019-07-18 05:26:35 +08:00
|
|
|
if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
|
|
|
|
return Expr<SomeType>{
|
|
|
|
ConvertToKind<TypeCategory::Real>(type.kind(), std::move(*boz))};
|
|
|
|
}
|
2019-05-14 00:33:18 +08:00
|
|
|
return ConvertToNumeric<TypeCategory::Real>(type.kind(), std::move(x));
|
2019-01-23 08:30:32 +08:00
|
|
|
case TypeCategory::Complex:
|
2019-05-14 00:33:18 +08:00
|
|
|
return ConvertToNumeric<TypeCategory::Complex>(type.kind(), std::move(x));
|
2019-01-23 08:30:32 +08:00
|
|
|
case TypeCategory::Character:
|
2019-02-20 07:38:55 +08:00
|
|
|
if (auto *cx{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
|
2019-02-28 07:51:03 +08:00
|
|
|
auto converted{
|
2019-05-14 00:33:18 +08:00
|
|
|
ConvertToKind<TypeCategory::Character>(type.kind(), std::move(*cx))};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (type.charLength()) {
|
2019-05-14 00:33:18 +08:00
|
|
|
if (const auto &len{type.charLength()->GetExplicit()}) {
|
2019-02-28 07:51:03 +08:00
|
|
|
Expr<SomeInteger> lenParam{*len};
|
|
|
|
Expr<SubscriptInteger> length{Convert<SubscriptInteger>{lenParam}};
|
|
|
|
converted = std::visit(
|
|
|
|
[&](auto &&x) {
|
|
|
|
using Ty = std::decay_t<decltype(x)>;
|
|
|
|
using CharacterType = typename Ty::Result;
|
|
|
|
return Expr<SomeCharacter>{
|
|
|
|
Expr<CharacterType>{SetLength<CharacterType::kind>{
|
|
|
|
std::move(x), std::move(length)}}};
|
|
|
|
},
|
|
|
|
std::move(converted.u));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Expr<SomeType>{std::move(converted)};
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TypeCategory::Logical:
|
|
|
|
if (auto *cx{UnwrapExpr<Expr<SomeLogical>>(x)}) {
|
|
|
|
return Expr<SomeType>{
|
2019-05-14 00:33:18 +08:00
|
|
|
ConvertToKind<TypeCategory::Logical>(type.kind(), std::move(*cx))};
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TypeCategory::Derived:
|
|
|
|
if (auto fromType{x.GetType()}) {
|
2019-02-20 07:38:55 +08:00
|
|
|
if (type == *fromType) {
|
2019-01-23 08:30:32 +08:00
|
|
|
return std::move(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-05-14 00:33:18 +08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
|
|
|
const DynamicType &to, std::optional<Expr<SomeType>> &&x) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (x) {
|
2019-05-14 00:33:18 +08:00
|
|
|
return ConvertToType(to, std::move(*x));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-20 07:38:55 +08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &symbol, Expr<SomeType> &&x) {
|
2019-05-04 02:29:15 +08:00
|
|
|
if (auto symType{DynamicType::From(symbol)}) {
|
2019-02-20 07:38:55 +08:00
|
|
|
return ConvertToType(*symType, std::move(x));
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-01-23 08:30:32 +08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &to, std::optional<Expr<SomeType>> &&x) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (x) {
|
2019-05-14 00:33:18 +08:00
|
|
|
return ConvertToType(to, std::move(*x));
|
2019-01-23 08:30:32 +08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2019-05-04 02:29:15 +08:00
|
|
|
|
2019-10-23 07:53:29 +08:00
|
|
|
bool IsAssumedRank(const Symbol &symbol0) {
|
|
|
|
const Symbol &symbol{ResolveAssociations(symbol0)};
|
2019-05-04 02:29:15 +08:00
|
|
|
if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
|
|
|
return details->IsAssumedRank();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsAssumedRank(const ActualArgument &arg) {
|
2019-05-22 07:58:46 +08:00
|
|
|
if (const auto *expr{arg.UnwrapExpr()}) {
|
2019-05-04 02:29:15 +08:00
|
|
|
return IsAssumedRank(*expr);
|
|
|
|
} else {
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol *assumedTypeDummy{arg.GetAssumedTypeDummy()};
|
2019-11-10 01:29:31 +08:00
|
|
|
CHECK(assumedTypeDummy);
|
2019-05-04 02:29:15 +08:00
|
|
|
return IsAssumedRank(*assumedTypeDummy);
|
|
|
|
}
|
|
|
|
}
|
2019-07-24 01:55:56 +08:00
|
|
|
|
2019-11-08 08:01:38 +08:00
|
|
|
bool IsProcedure(const Expr<SomeType> &expr) {
|
|
|
|
return std::holds_alternative<ProcedureDesignator>(expr.u);
|
|
|
|
}
|
2020-08-19 01:47:52 +08:00
|
|
|
bool IsFunction(const Expr<SomeType> &expr) {
|
|
|
|
const auto *designator{std::get_if<ProcedureDesignator>(&expr.u)};
|
|
|
|
return designator && designator->GetType().has_value();
|
|
|
|
}
|
2019-11-08 08:01:38 +08:00
|
|
|
|
2019-10-09 06:21:09 +08:00
|
|
|
bool IsProcedurePointer(const Expr<SomeType> &expr) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](const NullPointer &) { return true; },
|
|
|
|
[](const ProcedureDesignator &) { return true; },
|
|
|
|
[](const ProcedureRef &) { return true; },
|
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
2019-10-09 06:21:09 +08:00
|
|
|
expr.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsNullPointer()
|
2019-10-23 01:34:05 +08:00
|
|
|
struct IsNullPointerHelper : public AllTraverse<IsNullPointerHelper, false> {
|
|
|
|
using Base = AllTraverse<IsNullPointerHelper, false>;
|
|
|
|
IsNullPointerHelper() : Base(*this) {}
|
|
|
|
using Base::operator();
|
|
|
|
bool operator()(const ProcedureRef &call) const {
|
|
|
|
auto *intrinsic{call.proc().GetSpecificIntrinsic()};
|
|
|
|
return intrinsic &&
|
|
|
|
intrinsic->characteristics.value().attrs.test(
|
|
|
|
characteristics::Procedure::Attr::NullPointer);
|
|
|
|
}
|
|
|
|
bool operator()(const NullPointer &) const { return true; }
|
|
|
|
};
|
2019-10-09 06:21:09 +08:00
|
|
|
bool IsNullPointer(const Expr<SomeType> &expr) {
|
2019-10-23 01:34:05 +08:00
|
|
|
return IsNullPointerHelper{}(expr);
|
2019-10-09 06:21:09 +08:00
|
|
|
}
|
|
|
|
|
2019-11-02 04:08:16 +08:00
|
|
|
// GetSymbolVector()
|
|
|
|
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
|
2020-01-16 05:43:05 +08:00
|
|
|
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
|
|
|
|
return (*this)(details->expr());
|
|
|
|
} else {
|
|
|
|
return {x.GetUltimate()};
|
|
|
|
}
|
2019-07-24 01:55:56 +08:00
|
|
|
}
|
2019-11-02 04:08:16 +08:00
|
|
|
auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
|
|
|
|
Result result{(*this)(x.base())};
|
|
|
|
result.emplace_back(x.GetLastSymbol());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
auto GetSymbolVectorHelper::operator()(const ArrayRef &x) const -> Result {
|
|
|
|
return GetSymbolVector(x.base());
|
|
|
|
}
|
|
|
|
auto GetSymbolVectorHelper::operator()(const CoarrayRef &x) const -> Result {
|
|
|
|
return x.base();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol *GetLastTarget(const SymbolVector &symbols) {
|
|
|
|
auto end{std::crend(symbols)};
|
|
|
|
// N.B. Neither clang nor g++ recognizes "symbols.crbegin()" here.
|
|
|
|
auto iter{std::find_if(std::crbegin(symbols), end, [](const Symbol &x) {
|
|
|
|
return x.attrs().HasAny(
|
|
|
|
{semantics::Attr::POINTER, semantics::Attr::TARGET});
|
|
|
|
})};
|
|
|
|
return iter == end ? nullptr : &**iter;
|
2019-07-24 01:55:56 +08:00
|
|
|
}
|
|
|
|
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &ResolveAssociations(const Symbol &symbol) {
|
2019-08-01 05:13:42 +08:00
|
|
|
if (const auto *details{symbol.detailsIf<semantics::AssocEntityDetails>()}) {
|
|
|
|
if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) {
|
|
|
|
return ResolveAssociations(*nested);
|
|
|
|
}
|
|
|
|
}
|
2020-01-04 03:34:16 +08:00
|
|
|
return symbol.GetUltimate();
|
2019-08-01 05:13:42 +08:00
|
|
|
}
|
|
|
|
|
2019-09-20 03:19:17 +08:00
|
|
|
struct CollectSymbolsHelper
|
2020-03-28 05:17:25 +08:00
|
|
|
: public SetTraverse<CollectSymbolsHelper, semantics::SymbolSet> {
|
2019-10-23 07:53:29 +08:00
|
|
|
using Base = SetTraverse<CollectSymbolsHelper, semantics::SymbolSet>;
|
2019-09-20 03:19:17 +08:00
|
|
|
CollectSymbolsHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
2019-10-23 07:53:29 +08:00
|
|
|
semantics::SymbolSet operator()(const Symbol &symbol) const {
|
|
|
|
return {symbol};
|
2019-09-20 03:19:17 +08:00
|
|
|
}
|
|
|
|
};
|
2020-03-28 05:17:25 +08:00
|
|
|
template <typename A> semantics::SymbolSet CollectSymbols(const A &x) {
|
2019-09-20 03:19:17 +08:00
|
|
|
return CollectSymbolsHelper{}(x);
|
|
|
|
}
|
2019-10-23 07:53:29 +08:00
|
|
|
template semantics::SymbolSet CollectSymbols(const Expr<SomeType> &);
|
|
|
|
template semantics::SymbolSet CollectSymbols(const Expr<SomeInteger> &);
|
|
|
|
template semantics::SymbolSet CollectSymbols(const Expr<SubscriptInteger> &);
|
2019-09-20 03:19:17 +08:00
|
|
|
|
2019-10-11 04:09:35 +08:00
|
|
|
// HasVectorSubscript()
|
|
|
|
struct HasVectorSubscriptHelper : public AnyTraverse<HasVectorSubscriptHelper> {
|
|
|
|
using Base = AnyTraverse<HasVectorSubscriptHelper>;
|
|
|
|
HasVectorSubscriptHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
|
|
|
bool operator()(const Subscript &ss) const {
|
|
|
|
return !std::holds_alternative<Triplet>(ss.u) && ss.Rank() > 0;
|
|
|
|
}
|
|
|
|
bool operator()(const ProcedureRef &) const {
|
2020-03-28 05:17:25 +08:00
|
|
|
return false; // don't descend into function call arguments
|
2019-10-11 04:09:35 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool HasVectorSubscript(const Expr<SomeType> &expr) {
|
|
|
|
return HasVectorSubscriptHelper{}(expr);
|
|
|
|
}
|
2019-11-02 04:08:16 +08:00
|
|
|
|
|
|
|
parser::Message *AttachDeclaration(
|
2019-11-23 05:20:58 +08:00
|
|
|
parser::Message &message, const Symbol &symbol) {
|
|
|
|
const Symbol *unhosted{&symbol};
|
|
|
|
while (
|
|
|
|
const auto *assoc{unhosted->detailsIf<semantics::HostAssocDetails>()}) {
|
|
|
|
unhosted = &assoc->symbol();
|
|
|
|
}
|
|
|
|
if (const auto *binding{
|
|
|
|
unhosted->detailsIf<semantics::ProcBindingDetails>()}) {
|
|
|
|
if (binding->symbol().name() != symbol.name()) {
|
|
|
|
message.Attach(binding->symbol().name(),
|
2020-09-10 22:22:52 +08:00
|
|
|
"Procedure '%s' of type '%s' is bound to '%s'"_en_US, symbol.name(),
|
|
|
|
symbol.owner().GetName().value(), binding->symbol().name());
|
2019-11-23 05:20:58 +08:00
|
|
|
return &message;
|
2019-11-02 04:08:16 +08:00
|
|
|
}
|
2019-11-23 05:20:58 +08:00
|
|
|
unhosted = &binding->symbol();
|
|
|
|
}
|
|
|
|
if (const auto *use{symbol.detailsIf<semantics::UseDetails>()}) {
|
|
|
|
message.Attach(use->location(),
|
|
|
|
"'%s' is USE-associated with '%s' in module '%s'"_en_US, symbol.name(),
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
unhosted->name(), GetUsedModule(*use).name());
|
2019-11-23 05:20:58 +08:00
|
|
|
} else {
|
|
|
|
message.Attach(
|
|
|
|
unhosted->name(), "Declaration of '%s'"_en_US, unhosted->name());
|
2019-11-02 04:08:16 +08:00
|
|
|
}
|
2019-11-13 07:43:09 +08:00
|
|
|
return &message;
|
|
|
|
}
|
|
|
|
|
|
|
|
parser::Message *AttachDeclaration(
|
2019-11-23 05:20:58 +08:00
|
|
|
parser::Message *message, const Symbol &symbol) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
return message ? AttachDeclaration(*message, symbol) : nullptr;
|
2019-11-02 04:08:16 +08:00
|
|
|
}
|
2019-11-16 06:26:10 +08:00
|
|
|
|
|
|
|
class FindImpureCallHelper
|
2020-03-28 05:17:25 +08:00
|
|
|
: public AnyTraverse<FindImpureCallHelper, std::optional<std::string>> {
|
2019-11-16 06:26:10 +08:00
|
|
|
using Result = std::optional<std::string>;
|
|
|
|
using Base = AnyTraverse<FindImpureCallHelper, Result>;
|
|
|
|
|
|
|
|
public:
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
explicit FindImpureCallHelper(FoldingContext &c) : Base{*this}, context_{c} {}
|
2019-11-16 06:26:10 +08:00
|
|
|
using Base::operator();
|
|
|
|
Result operator()(const ProcedureRef &call) const {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (auto chars{
|
|
|
|
characteristics::Procedure::Characterize(call.proc(), context_)}) {
|
2019-11-16 06:26:10 +08:00
|
|
|
if (chars->attrs.test(characteristics::Procedure::Attr::Pure)) {
|
2020-01-26 00:15:17 +08:00
|
|
|
return (*this)(call.arguments());
|
2019-11-16 06:26:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return call.proc().GetName();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
FoldingContext &context_;
|
2019-11-16 06:26:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
std::optional<std::string> FindImpureCall(
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
FoldingContext &context, const Expr<SomeType> &expr) {
|
|
|
|
return FindImpureCallHelper{context}(expr);
|
2019-11-16 06:26:10 +08:00
|
|
|
}
|
2020-02-19 09:14:24 +08:00
|
|
|
std::optional<std::string> FindImpureCall(
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
FoldingContext &context, const ProcedureRef &proc) {
|
|
|
|
return FindImpureCallHelper{context}(proc);
|
2020-02-19 09:14:24 +08:00
|
|
|
}
|
2019-11-16 06:26:10 +08:00
|
|
|
|
2020-09-26 00:03:17 +08:00
|
|
|
// Compare procedure characteristics for equality except that lhs may be
|
|
|
|
// Pure or Elemental when rhs is not.
|
|
|
|
static bool CharacteristicsMatch(const characteristics::Procedure &lhs,
|
|
|
|
const characteristics::Procedure &rhs) {
|
|
|
|
using Attr = characteristics::Procedure::Attr;
|
|
|
|
auto lhsAttrs{rhs.attrs};
|
|
|
|
lhsAttrs.set(
|
|
|
|
Attr::Pure, lhs.attrs.test(Attr::Pure) | rhs.attrs.test(Attr::Pure));
|
|
|
|
lhsAttrs.set(Attr::Elemental,
|
|
|
|
lhs.attrs.test(Attr::Elemental) | rhs.attrs.test(Attr::Elemental));
|
|
|
|
return lhsAttrs == rhs.attrs && lhs.functionResult == rhs.functionResult &&
|
|
|
|
lhs.dummyArguments == rhs.dummyArguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Common handling for procedure pointer compatibility of left- and right-hand
|
|
|
|
// sides. Returns nullopt if they're compatible. Otherwise, it returns a
|
|
|
|
// message that needs to be augmented by the names of the left and right sides
|
|
|
|
std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
|
|
|
|
const std::optional<characteristics::Procedure> &lhsProcedure,
|
|
|
|
const characteristics::Procedure *rhsProcedure) {
|
|
|
|
std::optional<parser::MessageFixedText> msg;
|
|
|
|
if (!lhsProcedure) {
|
|
|
|
msg = "In assignment to object %s, the target '%s' is a procedure"
|
|
|
|
" designator"_err_en_US;
|
|
|
|
} else if (!rhsProcedure) {
|
|
|
|
msg = "In assignment to procedure %s, the characteristics of the target"
|
|
|
|
" procedure '%s' could not be determined"_err_en_US;
|
|
|
|
} else if (CharacteristicsMatch(*lhsProcedure, *rhsProcedure)) {
|
|
|
|
// OK
|
|
|
|
} else if (isCall) {
|
|
|
|
msg = "Procedure %s associated with result of reference to function '%s'"
|
|
|
|
" that is an incompatible procedure pointer"_err_en_US;
|
|
|
|
} else if (lhsProcedure->IsPure() && !rhsProcedure->IsPure()) {
|
|
|
|
msg = "PURE procedure %s may not be associated with non-PURE"
|
|
|
|
" procedure designator '%s'"_err_en_US;
|
|
|
|
} else if (lhsProcedure->IsFunction() && !rhsProcedure->IsFunction()) {
|
|
|
|
msg = "Function %s may not be associated with subroutine"
|
|
|
|
" designator '%s'"_err_en_US;
|
|
|
|
} else if (!lhsProcedure->IsFunction() && rhsProcedure->IsFunction()) {
|
|
|
|
msg = "Subroutine %s may not be associated with function"
|
|
|
|
" designator '%s'"_err_en_US;
|
|
|
|
} else if (lhsProcedure->HasExplicitInterface() &&
|
|
|
|
!rhsProcedure->HasExplicitInterface()) {
|
|
|
|
msg = "Procedure %s with explicit interface may not be associated with"
|
|
|
|
" procedure designator '%s' with implicit interface"_err_en_US;
|
|
|
|
} else if (!lhsProcedure->HasExplicitInterface() &&
|
|
|
|
rhsProcedure->HasExplicitInterface()) {
|
|
|
|
msg = "Procedure %s with implicit interface may not be associated with"
|
|
|
|
" procedure designator '%s' with explicit interface"_err_en_US;
|
|
|
|
} else {
|
|
|
|
msg = "Procedure %s associated with incompatible procedure"
|
|
|
|
" designator '%s'"_err_en_US;
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2020-03-28 05:17:25 +08:00
|
|
|
} // namespace Fortran::evaluate
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
|
|
|
// When a construct association maps to a variable, and that variable
|
|
|
|
// is not an array with a vector-valued subscript, return the base
|
|
|
|
// Symbol of that variable, else nullptr. Descends into other construct
|
|
|
|
// associations when one associations maps to another.
|
|
|
|
static const Symbol *GetAssociatedVariable(
|
|
|
|
const semantics::AssocEntityDetails &details) {
|
|
|
|
if (const auto &expr{details.expr()}) {
|
|
|
|
if (IsVariable(*expr) && !HasVectorSubscript(*expr)) {
|
|
|
|
if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) {
|
|
|
|
return GetAssociationRoot(*varSymbol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol *GetAssociationRoot(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
const auto *details{ultimate.detailsIf<semantics::AssocEntityDetails>()};
|
|
|
|
return details ? GetAssociatedVariable(*details) : &ultimate;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsVariableName(const Symbol &symbol) {
|
|
|
|
const Symbol *root{GetAssociationRoot(symbol)};
|
|
|
|
return root && root->has<ObjectEntityDetails>() && !IsNamedConstant(*root);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsPureProcedure(const Symbol &symbol) {
|
|
|
|
if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
|
|
|
|
if (const Symbol * procInterface{procDetails->interface().symbol()}) {
|
|
|
|
// procedure component with a pure interface
|
|
|
|
return IsPureProcedure(*procInterface);
|
|
|
|
}
|
|
|
|
} else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
|
|
|
|
return IsPureProcedure(details->symbol());
|
|
|
|
} else if (!IsProcedure(symbol)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (IsStmtFunction(symbol)) {
|
|
|
|
// Section 15.7(1) states that a statement function is PURE if it does not
|
|
|
|
// reference an IMPURE procedure or a VOLATILE variable
|
|
|
|
if (const auto &expr{symbol.get<SubprogramDetails>().stmtFunction()}) {
|
|
|
|
for (const SymbolRef &ref : evaluate::CollectSymbols(*expr)) {
|
|
|
|
if (IsFunction(*ref) && !IsPureProcedure(*ref)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const Symbol *root{GetAssociationRoot(*ref)};
|
|
|
|
if (root && root->attrs().test(Attr::VOLATILE)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true; // statement function was not found to be impure
|
|
|
|
}
|
|
|
|
return symbol.attrs().test(Attr::PURE) ||
|
|
|
|
(symbol.attrs().test(Attr::ELEMENTAL) &&
|
|
|
|
!symbol.attrs().test(Attr::IMPURE));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsPureProcedure(const Scope &scope) {
|
|
|
|
const Symbol *symbol{scope.GetSymbol()};
|
|
|
|
return symbol && IsPureProcedure(*symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsFunction(const Symbol &symbol) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const SubprogramDetails &x) { return x.isFunction(); },
|
|
|
|
[&](const SubprogramNameDetails &) {
|
|
|
|
return symbol.test(Symbol::Flag::Function);
|
|
|
|
},
|
|
|
|
[](const ProcEntityDetails &x) {
|
|
|
|
const auto &ifc{x.interface()};
|
|
|
|
return ifc.type() || (ifc.symbol() && IsFunction(*ifc.symbol()));
|
|
|
|
},
|
|
|
|
[](const ProcBindingDetails &x) { return IsFunction(x.symbol()); },
|
|
|
|
[](const UseDetails &x) { return IsFunction(x.symbol()); },
|
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsProcedure(const Symbol &symbol) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const SubprogramDetails &) { return true; },
|
|
|
|
[](const SubprogramNameDetails &) { return true; },
|
|
|
|
[](const ProcEntityDetails &) { return true; },
|
|
|
|
[](const GenericDetails &) { return true; },
|
|
|
|
[](const ProcBindingDetails &) { return true; },
|
|
|
|
[](const UseDetails &x) { return IsProcedure(x.symbol()); },
|
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol *FindCommonBlockContaining(const Symbol &object) {
|
|
|
|
const auto *details{object.detailsIf<ObjectEntityDetails>()};
|
|
|
|
return details ? details->commonBlock() : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsProcedurePointer(const Symbol &symbol) {
|
|
|
|
return symbol.has<ProcEntityDetails>() && IsPointer(symbol);
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool IsSaved(const Symbol &original) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(original)}) {
|
|
|
|
const Symbol &symbol{*root};
|
|
|
|
const Scope *scope{&symbol.owner()};
|
|
|
|
auto scopeKind{scope->kind()};
|
|
|
|
if (scopeKind == Scope::Kind::Module) {
|
|
|
|
return true; // BLOCK DATA entities must all be in COMMON, handled below
|
|
|
|
} else if (symbol.attrs().test(Attr::SAVE)) {
|
|
|
|
return true;
|
|
|
|
} else if (scopeKind == Scope::Kind::DerivedType) {
|
|
|
|
return false; // this is a component
|
|
|
|
} else if (IsNamedConstant(symbol)) {
|
|
|
|
return false;
|
|
|
|
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
|
|
|
|
object && object->init()) {
|
|
|
|
return true;
|
|
|
|
} else if (IsProcedurePointer(symbol) &&
|
|
|
|
symbol.get<ProcEntityDetails>().init()) {
|
|
|
|
return true;
|
|
|
|
} else if (const Symbol * block{FindCommonBlockContaining(symbol)};
|
|
|
|
block && block->attrs().test(Attr::SAVE)) {
|
|
|
|
return true;
|
2020-06-23 02:32:54 +08:00
|
|
|
} else if (IsDummy(symbol) || IsFunctionResult(symbol)) {
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
2020-09-27 03:41:20 +08:00
|
|
|
} else if (scope->hasSAVE() ) {
|
|
|
|
return true;
|
2020-06-19 08:17:04 +08:00
|
|
|
}
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
return false;
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsDummy(const Symbol &symbol) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{[](const EntityDetails &x) { return x.isDummy(); },
|
|
|
|
[](const ObjectEntityDetails &x) { return x.isDummy(); },
|
|
|
|
[](const ProcEntityDetails &x) { return x.isDummy(); },
|
|
|
|
[](const HostAssocDetails &x) { return IsDummy(x.symbol()); },
|
|
|
|
[](const auto &) { return false; }},
|
|
|
|
symbol.details());
|
|
|
|
}
|
|
|
|
|
2020-07-10 02:08:41 +08:00
|
|
|
bool IsFunctionResult(const Symbol &symbol) {
|
|
|
|
return (symbol.has<ObjectEntityDetails>() &&
|
|
|
|
symbol.get<ObjectEntityDetails>().isFuncResult()) ||
|
|
|
|
(symbol.has<ProcEntityDetails>() &&
|
|
|
|
symbol.get<ProcEntityDetails>().isFuncResult());
|
|
|
|
}
|
|
|
|
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
bool IsKindTypeParameter(const Symbol &symbol) {
|
|
|
|
const auto *param{symbol.detailsIf<TypeParamDetails>()};
|
|
|
|
return param && param->attr() == common::TypeParamAttr::Kind;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsLenTypeParameter(const Symbol &symbol) {
|
|
|
|
const auto *param{symbol.detailsIf<TypeParamDetails>()};
|
|
|
|
return param && param->attr() == common::TypeParamAttr::Len;
|
|
|
|
}
|
|
|
|
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
int CountLenParameters(const DerivedTypeSpec &type) {
|
|
|
|
return std::count_if(type.parameters().begin(), type.parameters().end(),
|
|
|
|
[](const auto &pair) { return pair.second.isLen(); });
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
int CountNonConstantLenParameters(const DerivedTypeSpec &type) {
|
|
|
|
return std::count_if(
|
|
|
|
type.parameters().begin(), type.parameters().end(), [](const auto &pair) {
|
|
|
|
if (!pair.second.isLen()) {
|
|
|
|
return false;
|
|
|
|
} else if (const auto &expr{pair.second.GetExplicit()}) {
|
|
|
|
return !IsConstantExpr(*expr);
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
const Symbol &GetUsedModule(const UseDetails &details) {
|
|
|
|
return DEREF(details.symbol().owner().symbol());
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Fortran::semantics
|