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 {
|
|
|
|
|
2021-12-14 05:53:45 +08:00
|
|
|
// 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};
|
|
|
|
|
2021-05-13 03:10:28 +08:00
|
|
|
std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&ref) {
|
|
|
|
const Symbol &symbol{ref.GetLastSymbol()};
|
|
|
|
if (auto dyType{DynamicType::From(symbol)}) {
|
|
|
|
return TypedWrapper<Designator, DataRef>(*dyType, std::move(ref));
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &symbol) {
|
|
|
|
return AsGenericExpr(DataRef{symbol});
|
|
|
|
}
|
|
|
|
|
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)>;
|
2021-09-23 07:49:09 +08:00
|
|
|
if constexpr (common::HasMember<T, TypelessExpression>) {
|
|
|
|
return expr; // no parentheses around typeless
|
|
|
|
} else if constexpr (std::is_same_v<T, Expr<SomeDerived>>) {
|
|
|
|
return AsGenericExpr(Parentheses<SomeDerived>{std::move(x)});
|
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));
|
|
|
|
}
|
|
|
|
|
2021-09-14 04:45:30 +08:00
|
|
|
std::optional<DataRef> ExtractDataRef(
|
|
|
|
const ActualArgument &arg, bool intoSubstring) {
|
|
|
|
if (const Expr<SomeType> *expr{arg.UnwrapExpr()}) {
|
|
|
|
return ExtractDataRef(*expr, intoSubstring);
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2021-03-13 05:51:33 +08:00
|
|
|
const Symbol &root{GetAssociationRoot(symbol)};
|
|
|
|
return !IsNamedConstant(root) && root.has<semantics::ObjectEntityDetails>();
|
2020-03-06 09:55:51 +08:00
|
|
|
}
|
|
|
|
auto IsVariableHelper::operator()(const Component &x) const -> Result {
|
2021-03-13 05:51:33 +08:00
|
|
|
const Symbol &comp{x.GetLastSymbol()};
|
|
|
|
return (*this)(comp) && (IsPointer(comp) || (*this)(x.base()));
|
2020-03-06 09:55:51 +08:00
|
|
|
}
|
|
|
|
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 {
|
2021-03-13 05:51:33 +08:00
|
|
|
if (const Symbol * symbol{x.GetSymbol()}) {
|
|
|
|
const Symbol *result{FindFunctionResult(*symbol)};
|
|
|
|
return result && IsPointer(*result) && !IsProcedurePointer(*result);
|
|
|
|
}
|
|
|
|
return false;
|
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,
|
2021-12-14 05:53:45 +08:00
|
|
|
Expr<SomeKind<RCAT>> &&iry, [[maybe_unused]] 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
|
|
|
}
|
2021-12-14 05:53:45 +08:00
|
|
|
} else if constexpr (allowOperandDuplication &&
|
|
|
|
(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)));
|
2021-12-14 05:53:45 +08:00
|
|
|
} else {
|
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,
|
2021-12-14 05:53:45 +08:00
|
|
|
Expr<SomeComplex> &&zy, [[maybe_unused]] int defaultRealKind) {
|
|
|
|
if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>>) {
|
2018-09-06 08:12:03 +08:00
|
|
|
// x + (a,b) -> (a,b) + x -> (a+x, b)
|
2021-12-14 05:53:45 +08:00
|
|
|
return MixedComplexLeft<OPR, LCAT>(
|
|
|
|
messages, std::move(zy), std::move(irx), defaultRealKind);
|
|
|
|
} else if constexpr (allowOperandDuplication &&
|
|
|
|
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)
|
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
|
|
|
}
|
2021-12-14 05:53:45 +08:00
|
|
|
} else {
|
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 <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) {
|
2022-01-12 02:38:26 +08:00
|
|
|
if (type.IsTypelessIntrinsicArgument()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
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))};
|
2021-06-03 08:13:55 +08:00
|
|
|
if (auto length{type.GetCharLength()}) {
|
|
|
|
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));
|
2019-02-28 07:51:03 +08:00
|
|
|
}
|
|
|
|
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
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
bool IsAssumedRank(const Symbol &original) {
|
2021-09-14 04:45:30 +08:00
|
|
|
if (const auto *assoc{original.detailsIf<semantics::AssocEntityDetails>()}) {
|
|
|
|
if (assoc->rank()) {
|
|
|
|
return false; // in SELECT RANK case
|
|
|
|
}
|
|
|
|
}
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const Symbol &symbol{semantics::ResolveAssociations(original)};
|
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
|
|
|
|
2021-09-17 08:21:40 +08:00
|
|
|
bool IsCoarray(const ActualArgument &arg) {
|
2021-11-19 03:48:42 +08:00
|
|
|
const auto *expr{arg.UnwrapExpr()};
|
|
|
|
return expr && IsCoarray(*expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsCoarray(const Symbol &symbol) {
|
|
|
|
return GetAssociationRoot(symbol).Corank() > 0;
|
2021-09-17 08:21:40 +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
|
|
|
|
2021-03-13 05:51:33 +08:00
|
|
|
bool IsProcedurePointerTarget(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; },
|
2021-03-13 05:51:33 +08:00
|
|
|
[&](const auto &) {
|
|
|
|
const Symbol *last{GetLastSymbol(expr)};
|
|
|
|
return last && IsProcedurePointer(*last);
|
|
|
|
},
|
2020-03-29 12:00:16 +08:00
|
|
|
},
|
2019-10-09 06:21:09 +08:00
|
|
|
expr.u);
|
|
|
|
}
|
|
|
|
|
2021-01-20 09:14:41 +08:00
|
|
|
template <typename A> inline const ProcedureRef *UnwrapProcedureRef(const A &) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline const ProcedureRef *UnwrapProcedureRef(const FunctionRef<T> &func) {
|
|
|
|
return &func;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline const ProcedureRef *UnwrapProcedureRef(const Expr<T> &expr) {
|
|
|
|
return std::visit(
|
|
|
|
[](const auto &x) { return UnwrapProcedureRef(x); }, expr.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsObjectPointer()
|
|
|
|
bool IsObjectPointer(const Expr<SomeType> &expr, FoldingContext &context) {
|
|
|
|
if (IsNullPointer(expr)) {
|
|
|
|
return true;
|
2021-03-13 05:51:33 +08:00
|
|
|
} else if (IsProcedurePointerTarget(expr)) {
|
2021-01-20 09:14:41 +08:00
|
|
|
return false;
|
2021-03-13 05:51:33 +08:00
|
|
|
} else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
|
|
|
|
return IsVariable(*funcRef);
|
2021-01-20 09:14:41 +08:00
|
|
|
} else if (const Symbol * symbol{GetLastSymbol(expr)}) {
|
|
|
|
return IsPointer(symbol->GetUltimate());
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 01:32:59 +08:00
|
|
|
bool IsBareNullPointer(const Expr<SomeType> *expr) {
|
|
|
|
return expr && std::holds_alternative<NullPointer>(expr->u);
|
|
|
|
}
|
|
|
|
|
2019-10-09 06:21:09 +08:00
|
|
|
// IsNullPointer()
|
2021-09-08 03:17:31 +08:00
|
|
|
struct IsNullPointerHelper {
|
|
|
|
template <typename A> bool operator()(const A &) const { return false; }
|
|
|
|
template <typename T> bool operator()(const FunctionRef<T> &call) const {
|
|
|
|
const auto *intrinsic{call.proc().GetSpecificIntrinsic()};
|
2019-10-23 01:34:05 +08:00
|
|
|
return intrinsic &&
|
|
|
|
intrinsic->characteristics.value().attrs.test(
|
|
|
|
characteristics::Procedure::Attr::NullPointer);
|
|
|
|
}
|
|
|
|
bool operator()(const NullPointer &) const { return true; }
|
2021-09-08 03:17:31 +08:00
|
|
|
template <typename T> bool operator()(const Parentheses<T> &x) const {
|
|
|
|
return (*this)(x.left());
|
|
|
|
}
|
|
|
|
template <typename T> bool operator()(const Expr<T> &x) const {
|
|
|
|
return std::visit(*this, x.u);
|
|
|
|
}
|
2019-10-23 01:34:05 +08:00
|
|
|
};
|
2021-09-08 03:17:31 +08:00
|
|
|
|
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-09-20 03:19:17 +08:00
|
|
|
struct CollectSymbolsHelper
|
2021-03-19 01:26:23 +08:00
|
|
|
: public SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet> {
|
|
|
|
using Base = SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet>;
|
2019-09-20 03:19:17 +08:00
|
|
|
CollectSymbolsHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
2021-03-19 01:26:23 +08:00
|
|
|
semantics::UnorderedSymbolSet operator()(const Symbol &symbol) const {
|
2019-10-23 07:53:29 +08:00
|
|
|
return {symbol};
|
2019-09-20 03:19:17 +08:00
|
|
|
}
|
|
|
|
};
|
2021-03-19 01:26:23 +08:00
|
|
|
template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &x) {
|
2019-09-20 03:19:17 +08:00
|
|
|
return CollectSymbolsHelper{}(x);
|
|
|
|
}
|
2021-03-19 01:26:23 +08:00
|
|
|
template semantics::UnorderedSymbolSet CollectSymbols(const Expr<SomeType> &);
|
|
|
|
template semantics::UnorderedSymbolSet CollectSymbols(
|
|
|
|
const Expr<SomeInteger> &);
|
|
|
|
template semantics::UnorderedSymbolSet 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
|
|
|
|
2021-11-09 23:10:42 +08:00
|
|
|
// Compare procedure characteristics for equality except that rhs may be
|
|
|
|
// Pure or Elemental when lhs is not.
|
2020-09-26 00:03:17 +08:00
|
|
|
static bool CharacteristicsMatch(const characteristics::Procedure &lhs,
|
|
|
|
const characteristics::Procedure &rhs) {
|
|
|
|
using Attr = characteristics::Procedure::Attr;
|
2021-11-09 23:10:42 +08:00
|
|
|
auto lhsAttrs{lhs.attrs};
|
2020-09-26 00:03:17 +08:00
|
|
|
lhsAttrs.set(
|
2021-10-23 07:39:16 +08:00
|
|
|
Attr::Pure, lhs.attrs.test(Attr::Pure) || rhs.attrs.test(Attr::Pure));
|
2020-09-26 00:03:17 +08:00
|
|
|
lhsAttrs.set(Attr::Elemental,
|
2021-10-23 07:39:16 +08:00
|
|
|
lhs.attrs.test(Attr::Elemental) || rhs.attrs.test(Attr::Elemental));
|
2020-09-26 00:03:17 +08:00
|
|
|
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()) {
|
2021-11-12 02:46:26 +08:00
|
|
|
// Section 10.2.2.4, paragraph 3 prohibits associating a procedure pointer
|
2022-02-10 11:46:34 +08:00
|
|
|
// with an explicit interface with a procedure whose characteristics don't
|
|
|
|
// match. That's the case if the target procedure has an implicit
|
|
|
|
// interface. But this case is allowed by several other compilers as long
|
|
|
|
// as the explicit interface can be called via an implicit interface.
|
|
|
|
if (!lhsProcedure->CanBeCalledViaImplicitInterface()) {
|
|
|
|
msg = "Procedure %s with explicit interface that cannot be called via "
|
|
|
|
"an implicit interface cannot be associated with procedure "
|
|
|
|
"designator with an implicit interface"_err_en_US;
|
|
|
|
}
|
2020-09-26 00:03:17 +08:00
|
|
|
} else if (!lhsProcedure->HasExplicitInterface() &&
|
|
|
|
rhsProcedure->HasExplicitInterface()) {
|
2022-02-10 11:46:34 +08:00
|
|
|
// OK if the target can be called via an implicit interface
|
2021-11-12 02:46:26 +08:00
|
|
|
if (!rhsProcedure->CanBeCalledViaImplicitInterface()) {
|
|
|
|
msg = "Procedure %s with implicit interface may not be associated "
|
|
|
|
"with procedure designator '%s' with explicit interface that "
|
|
|
|
"cannot be called via an implicit interface"_err_en_US;
|
|
|
|
}
|
2020-09-26 00:03:17 +08:00
|
|
|
} else {
|
|
|
|
msg = "Procedure %s associated with incompatible procedure"
|
|
|
|
" designator '%s'"_err_en_US;
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2021-01-16 08:59:52 +08:00
|
|
|
// GetLastPointerSymbol()
|
|
|
|
static const Symbol *GetLastPointerSymbol(const Symbol &symbol) {
|
|
|
|
return IsPointer(GetAssociationRoot(symbol)) ? &symbol : nullptr;
|
|
|
|
}
|
|
|
|
static const Symbol *GetLastPointerSymbol(const SymbolRef &symbol) {
|
|
|
|
return GetLastPointerSymbol(*symbol);
|
|
|
|
}
|
|
|
|
static const Symbol *GetLastPointerSymbol(const Component &x) {
|
|
|
|
const Symbol &c{x.GetLastSymbol()};
|
|
|
|
return IsPointer(c) ? &c : GetLastPointerSymbol(x.base());
|
|
|
|
}
|
|
|
|
static const Symbol *GetLastPointerSymbol(const NamedEntity &x) {
|
|
|
|
const auto *c{x.UnwrapComponent()};
|
|
|
|
return c ? GetLastPointerSymbol(*c) : GetLastPointerSymbol(x.GetLastSymbol());
|
|
|
|
}
|
|
|
|
static const Symbol *GetLastPointerSymbol(const ArrayRef &x) {
|
|
|
|
return GetLastPointerSymbol(x.base());
|
|
|
|
}
|
|
|
|
static const Symbol *GetLastPointerSymbol(const CoarrayRef &x) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
const Symbol *GetLastPointerSymbol(const DataRef &x) {
|
|
|
|
return std::visit([](const auto &y) { return GetLastPointerSymbol(y); }, x.u);
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:25:40 +08:00
|
|
|
template <TypeCategory TO, TypeCategory FROM>
|
|
|
|
static std::optional<Expr<SomeType>> DataConstantConversionHelper(
|
|
|
|
FoldingContext &context, const DynamicType &toType,
|
|
|
|
const Expr<SomeType> &expr) {
|
|
|
|
DynamicType sizedType{FROM, toType.kind()};
|
|
|
|
if (auto sized{
|
|
|
|
Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) {
|
|
|
|
if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) {
|
|
|
|
return std::visit(
|
|
|
|
[](const auto &w) -> std::optional<Expr<SomeType>> {
|
|
|
|
using FromType = typename std::decay_t<decltype(w)>::Result;
|
|
|
|
static constexpr int kind{FromType::kind};
|
|
|
|
if constexpr (IsValidKindOfIntrinsicType(TO, kind)) {
|
|
|
|
if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) {
|
|
|
|
using FromWordType = typename FromType::Scalar;
|
|
|
|
using LogicalType = value::Logical<FromWordType::bits>;
|
|
|
|
using ElementType =
|
|
|
|
std::conditional_t<TO == TypeCategory::Logical, LogicalType,
|
|
|
|
typename LogicalType::Word>;
|
|
|
|
std::vector<ElementType> values;
|
|
|
|
auto at{fromConst->lbounds()};
|
|
|
|
auto shape{fromConst->shape()};
|
|
|
|
for (auto n{GetSize(shape)}; n-- > 0;
|
|
|
|
fromConst->IncrementSubscripts(at)) {
|
|
|
|
auto elt{fromConst->At(at)};
|
|
|
|
if constexpr (TO == TypeCategory::Logical) {
|
|
|
|
values.emplace_back(std::move(elt));
|
|
|
|
} else {
|
|
|
|
values.emplace_back(elt.word());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {AsGenericExpr(AsExpr(Constant<Type<TO, kind>>{
|
|
|
|
std::move(values), std::move(shape)}))};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
someExpr->u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeType>> DataConstantConversionExtension(
|
|
|
|
FoldingContext &context, const DynamicType &toType,
|
|
|
|
const Expr<SomeType> &expr0) {
|
|
|
|
Expr<SomeType> expr{Fold(context, Expr<SomeType>{expr0})};
|
|
|
|
if (!IsActuallyConstant(expr)) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
if (auto fromType{expr.GetType()}) {
|
|
|
|
if (toType.category() == TypeCategory::Logical &&
|
|
|
|
fromType->category() == TypeCategory::Integer) {
|
|
|
|
return DataConstantConversionHelper<TypeCategory::Logical,
|
|
|
|
TypeCategory::Integer>(context, toType, expr);
|
|
|
|
}
|
|
|
|
if (toType.category() == TypeCategory::Integer &&
|
|
|
|
fromType->category() == TypeCategory::Logical) {
|
|
|
|
return DataConstantConversionHelper<TypeCategory::Integer,
|
|
|
|
TypeCategory::Logical>(context, toType, expr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-02-03 02:52:28 +08:00
|
|
|
bool IsAllocatableOrPointerObject(
|
|
|
|
const Expr<SomeType> &expr, FoldingContext &context) {
|
|
|
|
const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
|
|
|
|
return (sym && semantics::IsAllocatableOrPointer(*sym)) ||
|
|
|
|
evaluate::IsObjectPointer(expr, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MayBePassedAsAbsentOptional(
|
|
|
|
const Expr<SomeType> &expr, FoldingContext &context) {
|
|
|
|
const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
|
|
|
|
// 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual
|
|
|
|
// may be passed to a non-allocatable/non-pointer optional dummy. Note that
|
|
|
|
// other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to
|
|
|
|
// ignore this point in intrinsic contexts (e.g CMPLX argument).
|
|
|
|
return (sym && semantics::IsOptional(*sym)) ||
|
|
|
|
IsAllocatableOrPointerObject(expr, context);
|
|
|
|
}
|
|
|
|
|
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 {
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const Symbol &ResolveAssociations(const Symbol &original) {
|
|
|
|
const Symbol &symbol{original.GetUltimate()};
|
|
|
|
if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
|
|
|
|
if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) {
|
|
|
|
return ResolveAssociations(*nested);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbol;
|
|
|
|
}
|
|
|
|
|
[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
|
|
|
// 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.
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
|
[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
|
|
|
if (const auto &expr{details.expr()}) {
|
|
|
|
if (IsVariable(*expr) && !HasVectorSubscript(*expr)) {
|
|
|
|
if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
return &GetAssociationRoot(*varSymbol);
|
[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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const Symbol &GetAssociationRoot(const Symbol &original) {
|
|
|
|
const Symbol &symbol{ResolveAssociations(original)};
|
|
|
|
if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
|
|
|
|
if (const Symbol * root{GetAssociatedVariable(*details)}) {
|
|
|
|
return *root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbol;
|
2020-12-10 00:38:59 +08:00
|
|
|
}
|
|
|
|
|
2021-11-12 04:36:15 +08:00
|
|
|
const Symbol *GetMainEntry(const Symbol *symbol) {
|
|
|
|
if (symbol) {
|
|
|
|
if (const auto *subpDetails{symbol->detailsIf<SubprogramDetails>()}) {
|
|
|
|
if (const Scope * scope{subpDetails->entryScope()}) {
|
|
|
|
if (const Symbol * main{scope->symbol()}) {
|
|
|
|
return main;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbol;
|
|
|
|
}
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
bool IsVariableName(const Symbol &original) {
|
|
|
|
const Symbol &symbol{ResolveAssociations(original)};
|
|
|
|
if (symbol.has<ObjectEntityDetails>()) {
|
|
|
|
return !IsNamedConstant(symbol);
|
|
|
|
} else if (const auto *assoc{symbol.detailsIf<AssocEntityDetails>()}) {
|
|
|
|
const auto &expr{assoc->expr()};
|
|
|
|
return expr && IsVariable(*expr) && !HasVectorSubscript(*expr);
|
|
|
|
} else {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
bool IsPureProcedure(const Symbol &original) {
|
2021-11-12 04:36:15 +08:00
|
|
|
// An ENTRY is pure if its containing subprogram is
|
|
|
|
const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
|
[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
|
|
|
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;
|
|
|
|
}
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
if (ref->GetUltimate().attrs().test(Attr::VOLATILE)) {
|
[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
|
|
|
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) {
|
2022-02-05 10:04:58 +08:00
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
return ultimate.test(Symbol::Flag::Function) ||
|
|
|
|
std::visit(common::visitors{
|
|
|
|
[](const SubprogramDetails &x) { return x.isFunction(); },
|
|
|
|
[](const ProcEntityDetails &x) {
|
|
|
|
const auto &ifc{x.interface()};
|
|
|
|
return ifc.type() ||
|
|
|
|
(ifc.symbol() && IsFunction(*ifc.symbol()));
|
|
|
|
},
|
|
|
|
[](const ProcBindingDetails &x) {
|
|
|
|
return IsFunction(x.symbol());
|
|
|
|
},
|
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
|
|
|
ultimate.details());
|
[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
|
|
|
}
|
|
|
|
|
2021-01-20 09:14:41 +08:00
|
|
|
bool IsFunction(const Scope &scope) {
|
|
|
|
const Symbol *symbol{scope.GetSymbol()};
|
|
|
|
return symbol && IsFunction(*symbol);
|
|
|
|
}
|
|
|
|
|
[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 IsProcedure(const Symbol &symbol) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
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 auto &) { return false; },
|
|
|
|
},
|
|
|
|
symbol.GetUltimate().details());
|
[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
|
|
|
}
|
|
|
|
|
2021-01-20 09:14:41 +08:00
|
|
|
bool IsProcedure(const Scope &scope) {
|
|
|
|
const Symbol *symbol{scope.GetSymbol()};
|
|
|
|
return symbol && IsProcedure(*symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol *FindCommonBlockContaining(const Symbol &original) {
|
|
|
|
const Symbol &root{GetAssociationRoot(original)};
|
|
|
|
const auto *details{root.detailsIf<ObjectEntityDetails>()};
|
[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
|
|
|
return details ? details->commonBlock() : nullptr;
|
|
|
|
}
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
bool IsProcedurePointer(const Symbol &original) {
|
2021-03-13 05:51:33 +08:00
|
|
|
const Symbol &symbol{GetAssociationRoot(original)};
|
[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
|
|
|
return symbol.has<ProcEntityDetails>() && IsPointer(symbol);
|
|
|
|
}
|
|
|
|
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +08:00
|
|
|
// 3.11 automatic data object
|
|
|
|
bool IsAutomatic(const Symbol &original) {
|
|
|
|
const Symbol &symbol{original.GetUltimate()};
|
|
|
|
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) {
|
|
|
|
if (const DeclTypeSpec * type{symbol.GetType()}) {
|
|
|
|
// If a type parameter value is not a constant expression, the
|
|
|
|
// object is automatic.
|
|
|
|
if (type->category() == DeclTypeSpec::Character) {
|
|
|
|
if (const auto &length{
|
|
|
|
type->characterTypeSpec().length().GetExplicit()}) {
|
|
|
|
if (!evaluate::IsConstantExpr(*length)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
|
|
|
for (const auto &pair : derived->parameters()) {
|
|
|
|
if (const auto &value{pair.second.GetExplicit()}) {
|
|
|
|
if (!evaluate::IsConstantExpr(*value)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If an array bound is not a constant expression, the object is
|
|
|
|
// automatic.
|
|
|
|
for (const ShapeSpec &dim : object->shape()) {
|
|
|
|
if (const auto &lb{dim.lbound().GetExplicit()}) {
|
|
|
|
if (!evaluate::IsConstantExpr(*lb)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (const auto &ub{dim.ubound().GetExplicit()}) {
|
|
|
|
if (!evaluate::IsConstantExpr(*ub)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-19 08:17:04 +08:00
|
|
|
bool IsSaved(const Symbol &original) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const Symbol &symbol{GetAssociationRoot(original)};
|
|
|
|
const Scope &scope{symbol.owner()};
|
|
|
|
auto scopeKind{scope.kind()};
|
|
|
|
if (symbol.has<AssocEntityDetails>()) {
|
|
|
|
return false; // ASSOCIATE(non-variable)
|
|
|
|
} else if (scopeKind == Scope::Kind::DerivedType) {
|
|
|
|
return false; // this is a component
|
2021-07-20 02:53:20 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::SAVE)) {
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +08:00
|
|
|
return true; // explicit SAVE attribute
|
|
|
|
} else if (IsDummy(symbol) || IsFunctionResult(symbol) ||
|
2021-11-27 04:40:11 +08:00
|
|
|
IsAutomatic(symbol) || IsNamedConstant(symbol)) {
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +08:00
|
|
|
return false;
|
|
|
|
} else if (scopeKind == Scope::Kind::Module ||
|
|
|
|
(scopeKind == Scope::Kind::MainProgram &&
|
2021-11-19 03:48:42 +08:00
|
|
|
(symbol.attrs().test(Attr::TARGET) || evaluate::IsCoarray(symbol)))) {
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +08:00
|
|
|
// 8.5.16p4
|
|
|
|
// In main programs, implied SAVE matters only for pointer
|
|
|
|
// initialization targets and coarrays.
|
|
|
|
// BLOCK DATA entities must all be in COMMON,
|
|
|
|
// which was checked above.
|
|
|
|
return true;
|
|
|
|
} else if (scope.kind() == Scope::Kind::Subprogram &&
|
|
|
|
scope.context().languageFeatures().IsEnabled(
|
|
|
|
common::LanguageFeature::DefaultSave) &&
|
|
|
|
!(scope.symbol() && scope.symbol()->attrs().test(Attr::RECURSIVE))) {
|
|
|
|
// -fno-automatic/-save/-Msave option applies to objects in
|
|
|
|
// executable subprograms unless they are explicitly RECURSIVE.
|
|
|
|
return true;
|
2021-11-27 04:40:11 +08:00
|
|
|
} else if (symbol.test(Symbol::Flag::InDataStmt)) {
|
|
|
|
return true;
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
|
|
|
|
object && object->init()) {
|
|
|
|
return true;
|
|
|
|
} else if (IsProcedurePointer(symbol) &&
|
|
|
|
symbol.get<ProcEntityDetails>().init()) {
|
|
|
|
return true;
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +08:00
|
|
|
} else if (scope.hasSAVE()) {
|
|
|
|
return true; // bare SAVE statement
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
} else if (const Symbol * block{FindCommonBlockContaining(symbol)};
|
|
|
|
block && block->attrs().test(Attr::SAVE)) {
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +08:00
|
|
|
return true; // in COMMON with SAVE
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
} else {
|
[flang] Add -fno-automatic, refine IsSaved()
This legacy option (available in other Fortran compilers with various
spellings) implies the SAVE attribute for local variables on subprograms
that are not explicitly RECURSIVE. The SAVE attribute essentially implies
static rather than stack storage. This was the default setting in Fortran
until surprisingly recently, so explicit SAVE statements & attributes
could be and often were omitted from older codes. Note that initialized
objects already have an implied SAVE attribute, and objects in COMMON
effectively do too, as data overlays are extinct; and since objects that are
expected to survive from one invocation of a procedure to the next in static
storage should probably be explicit initialized in the first place, so the
use cases for this option are somewhat rare, and all of them could be
handled with explicit SAVE statements or attributes.
This implicit SAVE attribute must not apply to automatic (in the Fortran sense)
local objects, whose sizes cannot be known at compilation time. To get the
semantics of IsSaved() right, the IsAutomatic() predicate was moved into
Evaluate/tools.cpp to allow for dynamic linking of the compiler. The
redundant predicate IsAutomatic() was noticed, removed, and its uses replaced.
GNU Fortran's spelling of the option (-fno-automatic) was added to
the clang-based driver and used for basic sanity testing.
Differential Revision: https://reviews.llvm.org/D114209
2021-11-19 03:48:42 +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(); },
|
2021-07-23 05:53:50 +08:00
|
|
|
[](const SubprogramDetails &x) { return x.isDummy(); },
|
[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 auto &) { return false; }},
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
ResolveAssociations(symbol).details());
|
[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
|
|
|
}
|
|
|
|
|
2021-11-27 05:26:50 +08:00
|
|
|
bool IsAssumedShape(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{ResolveAssociations(symbol)};
|
|
|
|
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
|
|
|
return object && object->CanBeAssumedShape() &&
|
|
|
|
!evaluate::IsAllocatableOrPointer(ultimate);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsDeferredShape(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{ResolveAssociations(symbol)};
|
|
|
|
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
|
|
|
return object && object->CanBeDeferredShape() &&
|
|
|
|
evaluate::IsAllocatableOrPointer(ultimate);
|
|
|
|
}
|
|
|
|
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
bool IsFunctionResult(const Symbol &original) {
|
|
|
|
const Symbol &symbol{GetAssociationRoot(original)};
|
2020-07-10 02:08:41 +08:00
|
|
|
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) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
|
[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 param && param->attr() == common::TypeParamAttr::Kind;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsLenTypeParameter(const Symbol &symbol) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
|
[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 param && param->attr() == common::TypeParamAttr::Len;
|
|
|
|
}
|
|
|
|
|
2021-09-22 07:06:30 +08:00
|
|
|
bool IsExtensibleType(const DerivedTypeSpec *derived) {
|
|
|
|
return derived && !IsIsoCType(derived) &&
|
|
|
|
!derived->typeSymbol().attrs().test(Attr::BIND_C) &&
|
|
|
|
!derived->typeSymbol().get<DerivedTypeDetails>().sequence();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
|
|
|
|
if (!derived) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
const auto &symbol{derived->typeSymbol()};
|
|
|
|
return &symbol.owner() == symbol.owner().context().GetBuiltinsScope() &&
|
|
|
|
symbol.name() == "__builtin_"s + name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsIsoCType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "c_ptr") ||
|
|
|
|
IsBuiltinDerivedType(derived, "c_funptr");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsTeamType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "team_type");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsBadCoarrayType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsTeamType(derived) || IsIsoCType(derived);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
|
|
|
|
return IsBuiltinDerivedType(derivedTypeSpec, "event_type") ||
|
|
|
|
IsBuiltinDerivedType(derivedTypeSpec, "lock_type");
|
|
|
|
}
|
|
|
|
|
[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;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-22 03:12:26 +08:00
|
|
|
// Are the type parameters of type1 compile-time compatible with the
|
|
|
|
// corresponding kind type parameters of type2? Return true if all constant
|
|
|
|
// valued parameters are equal.
|
|
|
|
// Used to check assignment statements and argument passing. See 15.5.2.4(4)
|
|
|
|
bool AreTypeParamCompatible(const semantics::DerivedTypeSpec &type1,
|
|
|
|
const semantics::DerivedTypeSpec &type2) {
|
|
|
|
for (const auto &[name, param1] : type1.parameters()) {
|
|
|
|
if (semantics::MaybeIntExpr paramExpr1{param1.GetExplicit()}) {
|
|
|
|
if (IsConstantExpr(*paramExpr1)) {
|
|
|
|
const semantics::ParamValue *param2{type2.FindParameter(name)};
|
|
|
|
if (param2) {
|
|
|
|
if (semantics::MaybeIntExpr paramExpr2{param2->GetExplicit()}) {
|
|
|
|
if (IsConstantExpr(*paramExpr2)) {
|
|
|
|
if (ToInt64(*paramExpr1) != ToInt64(*paramExpr2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2021-03-13 05:51:33 +08:00
|
|
|
static const Symbol *FindFunctionResult(
|
2021-03-19 01:26:23 +08:00
|
|
|
const Symbol &original, UnorderedSymbolSet &seen) {
|
2021-03-13 05:51:33 +08:00
|
|
|
const Symbol &root{GetAssociationRoot(original)};
|
|
|
|
;
|
|
|
|
if (!seen.insert(root).second) {
|
|
|
|
return nullptr; // don't loop
|
|
|
|
}
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{[](const SubprogramDetails &subp) {
|
|
|
|
return subp.isFunction() ? &subp.result() : nullptr;
|
|
|
|
},
|
|
|
|
[&](const ProcEntityDetails &proc) {
|
|
|
|
const Symbol *iface{proc.interface().symbol()};
|
|
|
|
return iface ? FindFunctionResult(*iface, seen) : nullptr;
|
|
|
|
},
|
|
|
|
[&](const ProcBindingDetails &binding) {
|
|
|
|
return FindFunctionResult(binding.symbol(), seen);
|
|
|
|
},
|
|
|
|
[](const auto &) -> const Symbol * { return nullptr; }},
|
|
|
|
root.details());
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol *FindFunctionResult(const Symbol &symbol) {
|
2021-03-19 01:26:23 +08:00
|
|
|
UnorderedSymbolSet seen;
|
2021-03-13 05:51:33 +08:00
|
|
|
return FindFunctionResult(symbol, seen);
|
|
|
|
}
|
|
|
|
|
2021-03-17 05:32:45 +08:00
|
|
|
// These are here in Evaluate/tools.cpp so that Evaluate can use
|
|
|
|
// them; they cannot be defined in symbol.h due to the dependence
|
|
|
|
// on Scope.
|
|
|
|
|
2021-03-19 01:26:23 +08:00
|
|
|
bool SymbolSourcePositionCompare::operator()(
|
|
|
|
const SymbolRef &x, const SymbolRef &y) const {
|
|
|
|
return x->GetSemanticsContext().allCookedSources().Precedes(
|
|
|
|
x->name(), y->name());
|
|
|
|
}
|
|
|
|
bool SymbolSourcePositionCompare::operator()(
|
|
|
|
const MutableSymbolRef &x, const MutableSymbolRef &y) const {
|
|
|
|
return x->GetSemanticsContext().allCookedSources().Precedes(
|
|
|
|
x->name(), y->name());
|
2021-03-17 05:32:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SemanticsContext &Symbol::GetSemanticsContext() const {
|
|
|
|
return DEREF(owner_).context();
|
|
|
|
}
|
|
|
|
|
[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
|