2020-02-25 15:11:52 +00:00
|
|
|
//===-- lib/Evaluate/tools.cpp --------------------------------------------===//
|
2018-08-10 11:44:43 -07:00
|
|
|
//
|
2019-12-20 12: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-10 11:44:43 -07:00
|
|
|
//
|
2020-01-10 12:12:03 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-08-10 11:44:43 -07:00
|
|
|
|
2020-02-25 15:11:52 +00:00
|
|
|
#include "flang/Evaluate/tools.h"
|
|
|
|
#include "flang/Common/idioms.h"
|
2025-04-04 08:42:38 -07:00
|
|
|
#include "flang/Common/type-kinds.h"
|
2020-02-25 15:11:52 +00:00
|
|
|
#include "flang/Evaluate/characteristics.h"
|
|
|
|
#include "flang/Evaluate/traverse.h"
|
|
|
|
#include "flang/Parser/message.h"
|
2020-03-05 17:55:51 -08:00
|
|
|
#include "flang/Semantics/tools.h"
|
2018-08-23 10:55:16 -07:00
|
|
|
#include <algorithm>
|
2018-08-10 11:44:43 -07:00
|
|
|
#include <variant>
|
|
|
|
|
|
|
|
using namespace Fortran::parser::literals;
|
|
|
|
|
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
2021-12-13 13: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-12 12:10:28 -07:00
|
|
|
std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&ref) {
|
2024-05-09 09:44:07 -07:00
|
|
|
if (auto dyType{DynamicType::From(ref.GetLastSymbol())}) {
|
2021-05-12 12:10:28 -07:00
|
|
|
return TypedWrapper<Designator, DataRef>(*dyType, std::move(ref));
|
2024-05-09 09:44:07 -07:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
2021-05-12 12:10:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &symbol) {
|
|
|
|
return AsGenericExpr(DataRef{symbol});
|
|
|
|
}
|
|
|
|
|
2019-11-22 14:58:26 -08:00
|
|
|
Expr<SomeType> Parenthesize(Expr<SomeType> &&expr) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2019-11-22 14:58:26 -08:00
|
|
|
[&](auto &&x) {
|
|
|
|
using T = std::decay_t<decltype(x)>;
|
2021-09-22 16:49:09 -07: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-22 14:58:26 -08:00
|
|
|
} else {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2019-11-22 14:58:26 -08:00
|
|
|
[](auto &&y) {
|
|
|
|
using T = ResultType<decltype(y)>;
|
|
|
|
return AsGenericExpr(Parentheses<T>{std::move(y)});
|
|
|
|
},
|
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
std::move(expr.u));
|
|
|
|
}
|
|
|
|
|
2021-09-13 13:45:30 -07:00
|
|
|
std::optional<DataRef> ExtractDataRef(
|
2022-11-16 13:48:45 -08:00
|
|
|
const ActualArgument &arg, bool intoSubstring, bool intoComplexPart) {
|
|
|
|
return ExtractDataRef(arg.UnwrapExpr(), intoSubstring, intoComplexPart);
|
2021-09-13 13:45:30 -07:00
|
|
|
}
|
|
|
|
|
2020-03-27 14:17:25 -07:00
|
|
|
std::optional<DataRef> ExtractSubstringBase(const Substring &substring) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2020-03-05 17:55:51 -08:00
|
|
|
common::visitors{
|
|
|
|
[&](const DataRef &x) -> std::optional<DataRef> { return x; },
|
|
|
|
[&](const StaticDataObject::Pointer &) -> std::optional<DataRef> {
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
substring.parent());
|
|
|
|
}
|
|
|
|
|
2019-07-23 10:55:56 -07:00
|
|
|
// IsVariable()
|
2020-03-05 17:55:51 -08:00
|
|
|
|
|
|
|
auto IsVariableHelper::operator()(const Symbol &symbol) const -> Result {
|
2023-02-09 10:00:12 -08:00
|
|
|
// ASSOCIATE(x => expr) -- x counts as a variable, but undefinable
|
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
return !IsNamedConstant(ultimate) &&
|
|
|
|
(ultimate.has<semantics::ObjectEntityDetails>() ||
|
2024-06-24 09:06:32 -07:00
|
|
|
(ultimate.has<semantics::EntityDetails>() &&
|
|
|
|
ultimate.attrs().test(semantics::Attr::TARGET)) ||
|
2023-02-09 10:00:12 -08:00
|
|
|
ultimate.has<semantics::AssocEntityDetails>());
|
2020-03-05 17:55:51 -08:00
|
|
|
}
|
|
|
|
auto IsVariableHelper::operator()(const Component &x) const -> Result {
|
2021-03-12 13:51:33 -08:00
|
|
|
const Symbol &comp{x.GetLastSymbol()};
|
|
|
|
return (*this)(comp) && (IsPointer(comp) || (*this)(x.base()));
|
2020-03-05 17: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-19 14:56:12 -07:00
|
|
|
auto IsVariableHelper::operator()(const ProcedureDesignator &x) const
|
|
|
|
-> Result {
|
2021-03-12 13: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-23 10:55:56 -07:00
|
|
|
}
|
|
|
|
|
2020-11-13 09:40:59 -08:00
|
|
|
// Conversions of COMPLEX component expressions to REAL.
|
2018-08-23 10:55:16 -07:00
|
|
|
ConvertRealOperandsResult ConvertRealOperands(
|
2018-08-20 09:29:08 -07:00
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&x,
|
2018-09-18 11:29:01 -07:00
|
|
|
Expr<SomeType> &&y, int defaultRealKind) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2018-11-29 09: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-28 15:15:18 -07:00
|
|
|
[&](Expr<SomeInteger> &&ix,
|
2024-12-18 07:02:37 -08:00
|
|
|
Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
|
|
|
},
|
|
|
|
[&](Expr<SomeUnsigned> &&ix,
|
|
|
|
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
|
|
|
},
|
|
|
|
[&](Expr<SomeUnsigned> &&ix,
|
|
|
|
Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
|
|
|
},
|
|
|
|
[&](Expr<SomeInteger> &&ix,
|
|
|
|
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertTo(ry, std::move(ix)), std::move(ry))};
|
|
|
|
},
|
|
|
|
[&](Expr<SomeUnsigned> &&ix,
|
2018-08-28 15:15:18 -07:00
|
|
|
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
|
2018-08-31 16:14:14 -07:00
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertTo(ry, std::move(ix)), std::move(ry))};
|
2018-08-10 11:44:43 -07:00
|
|
|
},
|
2018-08-28 15:15:18 -07:00
|
|
|
[&](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
|
2018-08-31 16:14:14 -07:00
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
std::move(rx), ConvertTo(rx, std::move(iy)))};
|
2018-08-10 11:44:43 -07:00
|
|
|
},
|
2024-12-18 07:02:37 -08:00
|
|
|
[&](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
std::move(rx), ConvertTo(rx, std::move(iy)))};
|
|
|
|
},
|
2018-08-28 15:15:18 -07:00
|
|
|
[&](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
|
2018-08-31 16:14:14 -07:00
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
std::move(rx), std::move(ry))};
|
2018-08-10 11:44:43 -07:00
|
|
|
},
|
2018-09-07 15:25:10 -07:00
|
|
|
[&](Expr<SomeInteger> &&ix,
|
|
|
|
BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
2018-09-18 11:29:01 -07:00
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(by)))};
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
2024-12-18 07:02:37 -08:00
|
|
|
[&](Expr<SomeUnsigned> &&ix,
|
|
|
|
BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(ix)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(by)))};
|
|
|
|
},
|
2018-09-07 15:25:10 -07:00
|
|
|
[&](BOZLiteralConstant &&bx,
|
|
|
|
Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
2018-09-18 11:29:01 -07:00
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(bx)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
2024-12-18 07:02:37 -08:00
|
|
|
[&](BOZLiteralConstant &&bx,
|
|
|
|
Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
|
|
|
|
return {AsSameKindExprs<TypeCategory::Real>(
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(bx)),
|
|
|
|
ConvertToKind<TypeCategory::Real>(
|
|
|
|
defaultRealKind, std::move(iy)))};
|
|
|
|
},
|
2018-09-07 15:25:10 -07: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))};
|
|
|
|
},
|
2025-01-27 09:00:10 -08:00
|
|
|
[&](BOZLiteralConstant &&,
|
|
|
|
BOZLiteralConstant &&) -> ConvertRealOperandsResult {
|
|
|
|
messages.Say("operands cannot both be BOZ"_err_en_US);
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
2020-03-27 14:17:25 -07:00
|
|
|
[&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718
|
2024-12-18 07:02:37 -08:00
|
|
|
messages.Say(
|
|
|
|
"operands must be INTEGER, UNSIGNED, REAL, or BOZ"_err_en_US);
|
2018-08-10 11:44:43 -07:00
|
|
|
return std::nullopt;
|
2018-11-29 09:27:34 -08:00
|
|
|
},
|
|
|
|
},
|
2018-08-10 11:44:43 -07:00
|
|
|
std::move(x.u), std::move(y.u));
|
|
|
|
}
|
|
|
|
|
2018-09-07 10:33:32 -07:00
|
|
|
// Helpers for NumericOperation and its subroutines below.
|
|
|
|
static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
|
|
|
|
|
2020-03-27 14:17:25 -07:00
|
|
|
template <TypeCategory CAT>
|
2018-08-30 10:09:44 -07:00
|
|
|
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
|
|
|
|
return {AsGenericExpr(std::move(catExpr))};
|
2018-08-23 10:55:16 -07:00
|
|
|
}
|
2020-03-27 14:17:25 -07:00
|
|
|
template <TypeCategory CAT>
|
2018-09-04 16:42:32 -07:00
|
|
|
std::optional<Expr<SomeType>> Package(
|
|
|
|
std::optional<Expr<SomeKind<CAT>>> &&catExpr) {
|
2019-11-09 09:29:31 -08:00
|
|
|
if (catExpr) {
|
2018-09-04 16:42:32 -07:00
|
|
|
return {AsGenericExpr(std::move(*catExpr))};
|
2023-09-13 16:34:23 -07:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
2018-09-04 16:42:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-07 15:25:10 -07:00
|
|
|
// Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that
|
|
|
|
// does not require conversion of the exponent expression.
|
2020-03-27 14:17:25 -07:00
|
|
|
template <template <typename> class OPR>
|
2018-09-07 15:25:10 -07:00
|
|
|
std::optional<Expr<SomeType>> MixedRealLeft(
|
|
|
|
Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return Package(common::visit(
|
2018-09-07 15:25:10 -07:00
|
|
|
[&](auto &&rxk) -> Expr<SomeReal> {
|
|
|
|
using resultType = ResultType<decltype(rxk)>;
|
|
|
|
if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
|
2018-09-17 11:31:38 -07:00
|
|
|
return AsCategoryExpr(
|
|
|
|
RealToIntPower<resultType>{std::move(rxk), std::move(iy)});
|
2018-09-07 15:25:10 -07: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-17 11:31:38 -07:00
|
|
|
return AsCategoryExpr(OPR<resultType>{
|
|
|
|
std::move(rxk), ConvertToType<resultType>(std::move(iy))});
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
std::move(rx.u)));
|
|
|
|
}
|
|
|
|
|
2023-09-13 16:34:23 -07:00
|
|
|
template <int KIND>
|
|
|
|
Expr<SomeComplex> MakeComplex(Expr<Type<TypeCategory::Real, KIND>> &&re,
|
|
|
|
Expr<Type<TypeCategory::Real, KIND>> &&im) {
|
|
|
|
return AsCategoryExpr(ComplexConstructor<KIND>{std::move(re), std::move(im)});
|
|
|
|
}
|
|
|
|
|
2018-09-04 16:42:32 -07:00
|
|
|
std::optional<Expr<SomeComplex>> ConstructComplex(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&real,
|
2018-09-18 11:29:01 -07:00
|
|
|
Expr<SomeType> &&imaginary, int defaultRealKind) {
|
2018-09-04 16:42:32 -07:00
|
|
|
if (auto converted{ConvertRealOperands(
|
2018-09-18 11:29:01 -07:00
|
|
|
messages, std::move(real), std::move(imaginary), defaultRealKind)}) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return {common::visit(
|
2018-09-04 16:42:32 -07:00
|
|
|
[](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-18 11:29:01 -07:00
|
|
|
std::optional<Expr<SomeType>> &&imaginary, int defaultRealKind) {
|
2018-09-04 16:42:32 -07:00
|
|
|
if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) {
|
2019-06-18 15:15:22 -07:00
|
|
|
return ConstructComplex(messages, std::get<0>(std::move(*parts)),
|
|
|
|
std::get<1>(std::move(*parts)), defaultRealKind);
|
2018-09-04 16:42:32 -07:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2023-09-13 16:34:23 -07:00
|
|
|
// Extracts the real or imaginary part of the result of a COMPLEX
|
|
|
|
// expression, when that expression is simple enough to be duplicated.
|
|
|
|
template <bool GET_IMAGINARY> struct ComplexPartExtractor {
|
|
|
|
template <typename A> static std::optional<Expr<SomeReal>> Get(const A &) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2018-09-04 16:42:32 -07:00
|
|
|
|
2023-09-13 16:34:23 -07:00
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(
|
|
|
|
const Parentheses<Type<TypeCategory::Complex, KIND>> &kz) {
|
|
|
|
if (auto x{Get(kz.left())}) {
|
|
|
|
return AsGenericExpr(AsSpecificExpr(
|
|
|
|
Parentheses<Type<TypeCategory::Real, KIND>>{std::move(*x)}));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(
|
|
|
|
const Negate<Type<TypeCategory::Complex, KIND>> &kz) {
|
|
|
|
if (auto x{Get(kz.left())}) {
|
|
|
|
return AsGenericExpr(AsSpecificExpr(
|
|
|
|
Negate<Type<TypeCategory::Real, KIND>>{std::move(*x)}));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(
|
|
|
|
const Convert<Type<TypeCategory::Complex, KIND>, TypeCategory::Complex>
|
|
|
|
&kz) {
|
|
|
|
if (auto x{Get(kz.left())}) {
|
|
|
|
return AsGenericExpr(AsSpecificExpr(
|
|
|
|
Convert<Type<TypeCategory::Real, KIND>, TypeCategory::Real>{
|
|
|
|
AsGenericExpr(std::move(*x))}));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(const ComplexConstructor<KIND> &kz) {
|
|
|
|
return GET_IMAGINARY ? Get(kz.right()) : Get(kz.left());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(
|
|
|
|
const Constant<Type<TypeCategory::Complex, KIND>> &kz) {
|
|
|
|
if (auto cz{kz.GetScalarValue()}) {
|
|
|
|
return AsGenericExpr(
|
|
|
|
AsSpecificExpr(GET_IMAGINARY ? cz->AIMAG() : cz->REAL()));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(
|
|
|
|
const Designator<Type<TypeCategory::Complex, KIND>> &kz) {
|
|
|
|
if (const auto *symbolRef{std::get_if<SymbolRef>(&kz.u)}) {
|
|
|
|
return AsGenericExpr(AsSpecificExpr(
|
|
|
|
Designator<Type<TypeCategory::Complex, KIND>>{ComplexPart{
|
|
|
|
DataRef{*symbolRef},
|
|
|
|
GET_IMAGINARY ? ComplexPart::Part::IM : ComplexPart::Part::RE}}));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int KIND>
|
|
|
|
static std::optional<Expr<SomeReal>> Get(
|
|
|
|
const Expr<Type<TypeCategory::Complex, KIND>> &kz) {
|
|
|
|
return Get(kz.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::optional<Expr<SomeReal>> Get(const Expr<SomeComplex> &z) {
|
|
|
|
return Get(z.u);
|
|
|
|
}
|
|
|
|
};
|
2022-05-06 18:39:23 -07:00
|
|
|
|
2019-12-06 01: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) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2019-12-06 01:18:20 -08:00
|
|
|
[](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-07 15:25:10 -07:00
|
|
|
// Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way
|
2018-09-05 17:12:03 -07:00
|
|
|
// than just converting the second operand to COMPLEX and performing the
|
|
|
|
// corresponding COMPLEX+COMPLEX operation.
|
2020-03-27 14:17:25 -07:00
|
|
|
template <template <typename> class OPR, TypeCategory RCAT>
|
2018-09-05 17:12:03 -07:00
|
|
|
std::optional<Expr<SomeType>> MixedComplexLeft(
|
2023-09-13 16:34:23 -07:00
|
|
|
parser::ContextualMessages &messages, const Expr<SomeComplex> &zx,
|
|
|
|
const Expr<SomeKind<RCAT>> &iry, [[maybe_unused]] int defaultRealKind) {
|
|
|
|
if constexpr (RCAT == TypeCategory::Integer &&
|
|
|
|
std::is_same_v<OPR<LargestReal>, Power<LargestReal>>) {
|
|
|
|
// COMPLEX**INTEGER is a special case that doesn't convert the exponent.
|
|
|
|
return Package(common::visit(
|
|
|
|
[&](const auto &zxk) {
|
|
|
|
using Ty = ResultType<decltype(zxk)>;
|
|
|
|
return AsCategoryExpr(AsExpr(
|
|
|
|
RealToIntPower<Ty>{common::Clone(zxk), common::Clone(iry)}));
|
|
|
|
},
|
|
|
|
zx.u));
|
|
|
|
}
|
|
|
|
std::optional<Expr<SomeReal>> zr{ComplexPartExtractor<false>{}.Get(zx)};
|
|
|
|
std::optional<Expr<SomeReal>> zi{ComplexPartExtractor<true>{}.Get(zx)};
|
|
|
|
if (!zr || !zi) {
|
|
|
|
} else if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>> ||
|
2018-10-15 17:11:24 -07:00
|
|
|
std::is_same_v<OPR<LargestReal>, Subtract<LargestReal>>) {
|
2018-09-05 17:12:03 -07:00
|
|
|
// (a,b) + x -> (a+x, b)
|
|
|
|
// (a,b) - x -> (a-x, b)
|
2018-10-15 17:11:24 -07:00
|
|
|
if (std::optional<Expr<SomeType>> rr{
|
2023-09-13 16:34:23 -07:00
|
|
|
NumericOperation<OPR>(messages, AsGenericExpr(std::move(*zr)),
|
|
|
|
AsGenericExpr(common::Clone(iry)), defaultRealKind)}) {
|
2018-10-15 17:11:24 -07:00
|
|
|
return Package(ConstructComplex(messages, std::move(*rr),
|
2023-09-13 16:34:23 -07:00
|
|
|
AsGenericExpr(std::move(*zi)), defaultRealKind));
|
2018-09-04 16:42:32 -07:00
|
|
|
}
|
2021-12-13 13: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-05 17:12:03 -07:00
|
|
|
// (a,b) * x -> (a*x, b*x)
|
|
|
|
// (a,b) / x -> (a/x, b/x)
|
2018-09-04 16:42:32 -07:00
|
|
|
auto copy{iry};
|
2023-09-13 16:34:23 -07:00
|
|
|
auto rr{NumericOperation<OPR>(messages, AsGenericExpr(std::move(*zr)),
|
|
|
|
AsGenericExpr(common::Clone(iry)), defaultRealKind)};
|
|
|
|
auto ri{NumericOperation<OPR>(messages, AsGenericExpr(std::move(*zi)),
|
2018-10-15 17:11:24 -07:00
|
|
|
AsGenericExpr(std::move(copy)), defaultRealKind)};
|
2018-09-04 16:42:32 -07:00
|
|
|
if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
|
2019-06-18 15:15:22 -07:00
|
|
|
return Package(ConstructComplex(messages, std::get<0>(std::move(*parts)),
|
|
|
|
std::get<1>(std::move(*parts)), defaultRealKind));
|
2018-09-04 16:42:32 -07:00
|
|
|
}
|
2018-09-05 17:12:03 -07:00
|
|
|
}
|
2023-09-13 16:34:23 -07:00
|
|
|
return std::nullopt;
|
2018-09-05 17:12:03 -07: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-07 15:25:10 -07:00
|
|
|
// x / (a,b) -> (x,0) / (a,b) (and **)
|
2020-03-27 14:17:25 -07:00
|
|
|
template <template <typename> class OPR, TypeCategory LCAT>
|
2018-09-05 17:12:03 -07:00
|
|
|
std::optional<Expr<SomeType>> MixedComplexRight(
|
2023-09-13 16:34:23 -07:00
|
|
|
parser::ContextualMessages &messages, const Expr<SomeKind<LCAT>> &irx,
|
|
|
|
const Expr<SomeComplex> &zy, [[maybe_unused]] int defaultRealKind) {
|
2021-12-13 13:53:45 -08:00
|
|
|
if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>>) {
|
2018-09-05 17:12:03 -07:00
|
|
|
// x + (a,b) -> (a,b) + x -> (a+x, b)
|
2023-09-13 16:34:23 -07:00
|
|
|
return MixedComplexLeft<OPR, LCAT>(messages, zy, irx, defaultRealKind);
|
2021-12-13 13:53:45 -08:00
|
|
|
} else if constexpr (allowOperandDuplication &&
|
|
|
|
std::is_same_v<OPR<LargestReal>, Multiply<LargestReal>>) {
|
2018-09-05 17:12:03 -07:00
|
|
|
// x * (a,b) -> (a,b) * x -> (a*x, b*x)
|
2023-09-13 16:34:23 -07:00
|
|
|
return MixedComplexLeft<OPR, LCAT>(messages, zy, irx, defaultRealKind);
|
2018-10-15 17:11:24 -07:00
|
|
|
} else if constexpr (std::is_same_v<OPR<LargestReal>,
|
|
|
|
Subtract<LargestReal>>) {
|
2018-09-05 17:12:03 -07:00
|
|
|
// x - (a,b) -> (x-a, -b)
|
2023-09-13 16:34:23 -07:00
|
|
|
std::optional<Expr<SomeReal>> zr{ComplexPartExtractor<false>{}.Get(zy)};
|
|
|
|
std::optional<Expr<SomeReal>> zi{ComplexPartExtractor<true>{}.Get(zy)};
|
|
|
|
if (zr && zi) {
|
|
|
|
if (std::optional<Expr<SomeType>> rr{NumericOperation<Subtract>(messages,
|
|
|
|
AsGenericExpr(common::Clone(irx)), AsGenericExpr(std::move(*zr)),
|
|
|
|
defaultRealKind)}) {
|
|
|
|
return Package(ConstructComplex(messages, std::move(*rr),
|
|
|
|
AsGenericExpr(-std::move(*zi)), defaultRealKind));
|
|
|
|
}
|
2019-12-06 01:18:20 -08:00
|
|
|
}
|
2018-09-04 16:42:32 -07:00
|
|
|
}
|
2023-09-13 16:34:23 -07:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promotes REAL(rk) and COMPLEX(zk) operands COMPLEX(max(rk,zk))
|
|
|
|
// then combine them with an operator.
|
|
|
|
template <template <typename> class OPR, TypeCategory XCAT, TypeCategory YCAT>
|
|
|
|
Expr<SomeComplex> PromoteMixedComplexReal(
|
|
|
|
Expr<SomeKind<XCAT>> &&x, Expr<SomeKind<YCAT>> &&y) {
|
|
|
|
static_assert(XCAT == TypeCategory::Complex || YCAT == TypeCategory::Complex);
|
|
|
|
static_assert(XCAT == TypeCategory::Real || YCAT == TypeCategory::Real);
|
|
|
|
return common::visit(
|
|
|
|
[&](const auto &kx, const auto &ky) {
|
|
|
|
constexpr int maxKind{std::max(
|
|
|
|
ResultType<decltype(kx)>::kind, ResultType<decltype(ky)>::kind)};
|
|
|
|
using ZTy = Type<TypeCategory::Complex, maxKind>;
|
|
|
|
return Expr<SomeComplex>{
|
|
|
|
Expr<ZTy>{OPR<ZTy>{ConvertToType<ZTy>(std::move(x)),
|
|
|
|
ConvertToType<ZTy>(std::move(y))}}};
|
|
|
|
},
|
|
|
|
x.u, y.u);
|
2018-09-04 16:42:32 -07:00
|
|
|
}
|
2018-08-23 10:55:16 -07:00
|
|
|
|
2018-09-04 14:20:48 -07:00
|
|
|
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
|
2018-09-07 15:25:10 -07:00
|
|
|
// the operands to a dyadic operation where one is permitted, it assumes the
|
|
|
|
// type and kind of the other operand.
|
2024-12-18 07:02:37 -08:00
|
|
|
template <template <typename> class OPR, bool CAN_BE_UNSIGNED>
|
2018-08-23 10:55:16 -07:00
|
|
|
std::optional<Expr<SomeType>> NumericOperation(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&x,
|
2018-10-15 17:11:24 -07:00
|
|
|
Expr<SomeType> &&y, int defaultRealKind) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2018-11-29 09: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-23 10:55:16 -07:00
|
|
|
[](Expr<SomeReal> &&rx, Expr<SomeReal> &&ry) {
|
2018-08-30 10:09:44 -07:00
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Real>(
|
|
|
|
std::move(rx), std::move(ry)));
|
2018-08-23 10:55:16 -07:00
|
|
|
},
|
2024-12-18 07:02:37 -08:00
|
|
|
[&](Expr<SomeUnsigned> &&ix, Expr<SomeUnsigned> &&iy) {
|
|
|
|
if constexpr (CAN_BE_UNSIGNED) {
|
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Unsigned>(
|
|
|
|
std::move(ix), std::move(iy)));
|
|
|
|
} else {
|
|
|
|
messages.Say("Operands must not be UNSIGNED"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
}
|
|
|
|
},
|
2018-09-07 15:25:10 -07:00
|
|
|
// Mixed REAL/INTEGER operations
|
2018-08-23 10:55:16 -07:00
|
|
|
[](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
2018-09-07 15:25:10 -07:00
|
|
|
return MixedRealLeft<OPR>(std::move(rx), std::move(iy));
|
2018-08-23 10:55:16 -07:00
|
|
|
},
|
|
|
|
[](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return Package(common::visit(
|
2018-08-23 10:55:16 -07:00
|
|
|
[&](auto &&ryk) -> Expr<SomeReal> {
|
2018-08-30 10:09:44 -07:00
|
|
|
using resultType = ResultType<decltype(ryk)>;
|
2018-09-17 11:31:38 -07:00
|
|
|
return AsCategoryExpr(
|
2018-09-04 16:42:32 -07:00
|
|
|
OPR<resultType>{ConvertToType<resultType>(std::move(ix)),
|
2018-09-17 11:31:38 -07:00
|
|
|
std::move(ryk)});
|
2018-08-23 10:55:16 -07:00
|
|
|
},
|
2018-08-30 10:09:44 -07:00
|
|
|
std::move(ry.u)));
|
2018-08-23 10:55:16 -07:00
|
|
|
},
|
2018-09-07 15:25:10 -07:00
|
|
|
// Homogeneous and mixed COMPLEX operations
|
2018-08-23 10:55:16 -07:00
|
|
|
[](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
|
2018-08-30 10:09:44 -07:00
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
|
|
|
|
std::move(zx), std::move(zy)));
|
2018-08-23 10:55:16 -07:00
|
|
|
},
|
2019-11-02 09:56:46 -07:00
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
|
2023-09-13 16:34:23 -07:00
|
|
|
if (auto result{
|
|
|
|
MixedComplexLeft<OPR>(messages, zx, iy, defaultRealKind)}) {
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
|
|
|
|
std::move(zx), ConvertTo(zx, std::move(iy))));
|
|
|
|
}
|
2018-09-04 16:42:32 -07:00
|
|
|
},
|
2019-11-02 09:56:46 -07:00
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
|
2023-09-13 16:34:23 -07:00
|
|
|
if (auto result{
|
|
|
|
MixedComplexLeft<OPR>(messages, zx, ry, defaultRealKind)}) {
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return Package(
|
|
|
|
PromoteMixedComplexReal<OPR>(std::move(zx), std::move(ry)));
|
|
|
|
}
|
2018-09-05 17:12:03 -07:00
|
|
|
},
|
2019-11-02 09:56:46 -07:00
|
|
|
[&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
|
2023-09-13 16:34:23 -07:00
|
|
|
if (auto result{MixedComplexRight<OPR>(
|
|
|
|
messages, ix, zy, defaultRealKind)}) {
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
|
|
|
|
ConvertTo(zy, std::move(ix)), std::move(zy)));
|
|
|
|
}
|
2018-09-05 17:12:03 -07:00
|
|
|
},
|
2019-11-02 09:56:46 -07:00
|
|
|
[&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
|
2023-09-13 16:34:23 -07:00
|
|
|
if (auto result{MixedComplexRight<OPR>(
|
|
|
|
messages, rx, zy, defaultRealKind)}) {
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return Package(
|
|
|
|
PromoteMixedComplexReal<OPR>(std::move(rx), std::move(zy)));
|
|
|
|
}
|
2018-09-04 16:42:32 -07:00
|
|
|
},
|
2018-09-05 17:12:03 -07:00
|
|
|
// Operations with one typeless operand
|
2018-09-04 14:20:48 -07:00
|
|
|
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
|
2024-12-18 07:02:37 -08:00
|
|
|
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
|
|
|
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
|
|
|
|
defaultRealKind);
|
|
|
|
},
|
|
|
|
[&](BOZLiteralConstant &&bx, Expr<SomeUnsigned> &&iy) {
|
|
|
|
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
2018-10-15 17:11:24 -07:00
|
|
|
AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
|
|
|
|
defaultRealKind);
|
2018-08-31 16:14:14 -07:00
|
|
|
},
|
2018-09-04 14:20:48 -07:00
|
|
|
[&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
|
2024-12-18 07:02:37 -08:00
|
|
|
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
2018-10-15 17:11:24 -07:00
|
|
|
AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y),
|
|
|
|
defaultRealKind);
|
2018-08-31 16:14:14 -07:00
|
|
|
},
|
2018-09-04 14:20:48 -07:00
|
|
|
[&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
|
2024-12-18 07:02:37 -08:00
|
|
|
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
|
|
|
std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))),
|
|
|
|
defaultRealKind);
|
|
|
|
},
|
|
|
|
[&](Expr<SomeUnsigned> &&ix, BOZLiteralConstant &&by) {
|
|
|
|
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
|
|
|
std::move(x), AsGenericExpr(ConvertTo(ix, std::move(by))),
|
|
|
|
defaultRealKind);
|
2018-09-04 14:20:48 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
|
2024-12-18 07:02:37 -08:00
|
|
|
return NumericOperation<OPR, CAN_BE_UNSIGNED>(messages,
|
|
|
|
std::move(x), AsGenericExpr(ConvertTo(rx, std::move(by))),
|
|
|
|
defaultRealKind);
|
|
|
|
},
|
|
|
|
// Error cases
|
|
|
|
[&](Expr<SomeUnsigned> &&, auto &&) {
|
|
|
|
messages.Say("Both operands must be UNSIGNED"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
|
|
|
[&](auto &&, Expr<SomeUnsigned> &&) {
|
|
|
|
messages.Say("Both operands must be UNSIGNED"_err_en_US);
|
|
|
|
return NoExpr();
|
2018-09-04 14:20:48 -07:00
|
|
|
},
|
2018-08-23 10:55:16 -07:00
|
|
|
[&](auto &&, auto &&) {
|
|
|
|
messages.Say("non-numeric operands to numeric operation"_err_en_US);
|
2018-09-07 10:33:32 -07:00
|
|
|
return NoExpr();
|
2018-11-29 09:27:34 -08:00
|
|
|
},
|
|
|
|
},
|
2018-08-23 10:55:16 -07:00
|
|
|
std::move(x.u), std::move(y.u));
|
|
|
|
}
|
|
|
|
|
2024-12-18 07:02:37 -08:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Power, false>(
|
2018-10-15 17:11:24 -07:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-08-31 16:14:14 -07:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
|
2018-10-15 17:11:24 -07:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-08-31 16:14:14 -07:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Divide>(
|
2018-10-15 17:11:24 -07:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-09-07 15:25:10 -07:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Add>(
|
2018-10-15 17:11:24 -07:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-09-07 15:25:10 -07:00
|
|
|
template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
|
2018-10-15 17:11:24 -07:00
|
|
|
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
|
|
|
|
int defaultRealKind);
|
2018-08-23 10:55:16 -07:00
|
|
|
|
2018-09-07 10:33:32 -07:00
|
|
|
std::optional<Expr<SomeType>> Negation(
|
|
|
|
parser::ContextualMessages &messages, Expr<SomeType> &&x) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2018-09-12 16:27:51 -07:00
|
|
|
common::visitors{
|
|
|
|
[&](BOZLiteralConstant &&) {
|
|
|
|
messages.Say("BOZ literal cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2019-02-19 17:06:28 -08:00
|
|
|
[&](NullPointer &&) {
|
|
|
|
messages.Say("NULL() cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2019-05-03 11:29:15 -07: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-05 13:11:55 -08:00
|
|
|
[&](Expr<SomeInteger> &&x) { return Package(-std::move(x)); },
|
2018-09-07 10:33:32 -07:00
|
|
|
[&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
|
|
|
|
[&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
|
2019-08-15 13:50:27 -07:00
|
|
|
[&](Expr<SomeCharacter> &&) {
|
2018-09-07 10:33:32 -07:00
|
|
|
messages.Say("CHARACTER cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
|
|
|
},
|
2019-08-15 13:50:27 -07:00
|
|
|
[&](Expr<SomeLogical> &&) {
|
2018-09-07 10:33:32 -07:00
|
|
|
messages.Say("LOGICAL cannot be negated"_err_en_US);
|
|
|
|
return NoExpr();
|
2018-09-12 16:27:51 -07:00
|
|
|
},
|
2024-12-18 07:02:37 -08:00
|
|
|
[&](Expr<SomeUnsigned> &&x) { return Package(-std::move(x)); },
|
2019-08-15 13:50:27 -07:00
|
|
|
[&](Expr<SomeDerived> &&) {
|
2019-05-13 09:33:18 -07:00
|
|
|
messages.Say("Operand cannot be negated"_err_en_US);
|
2018-09-12 16:27:51 -07:00
|
|
|
return NoExpr();
|
|
|
|
},
|
|
|
|
},
|
2018-09-07 10:33:32 -07:00
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
|
2018-09-07 16:54:33 -07:00
|
|
|
Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&x) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2019-01-07 10:15:27 -08:00
|
|
|
[](auto &&xk) { return AsCategoryExpr(LogicalNegation(std::move(xk))); },
|
2018-09-07 16:54:33 -07:00
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
|
2020-03-27 14:17:25 -07:00
|
|
|
template <TypeCategory CAT>
|
2018-09-07 15:25:10 -07:00
|
|
|
Expr<LogicalResult> PromoteAndRelate(
|
|
|
|
RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2018-09-07 15:25:10 -07:00
|
|
|
[=](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) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2018-11-29 09:27:34 -08:00
|
|
|
common::visitors{
|
2019-06-03 10:51:51 -07:00
|
|
|
[=](Expr<SomeInteger> &&ix,
|
|
|
|
Expr<SomeInteger> &&iy) -> std::optional<Expr<LogicalResult>> {
|
|
|
|
return PromoteAndRelate(opr, std::move(ix), std::move(iy));
|
2018-11-29 09:27:34 -08:00
|
|
|
},
|
2024-12-18 07:02:37 -08:00
|
|
|
[=](Expr<SomeUnsigned> &&ix,
|
|
|
|
Expr<SomeUnsigned> &&iy) -> std::optional<Expr<LogicalResult>> {
|
|
|
|
return PromoteAndRelate(opr, std::move(ix), std::move(iy));
|
|
|
|
},
|
2019-06-03 10:51:51 -07:00
|
|
|
[=](Expr<SomeReal> &&rx,
|
|
|
|
Expr<SomeReal> &&ry) -> std::optional<Expr<LogicalResult>> {
|
|
|
|
return PromoteAndRelate(opr, std::move(rx), std::move(ry));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
|
2018-09-12 16:27:51 -07:00
|
|
|
return Relate(messages, opr, std::move(x),
|
|
|
|
AsGenericExpr(ConvertTo(rx, std::move(iy))));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
|
2018-09-12 16:27:51 -07:00
|
|
|
return Relate(messages, opr,
|
|
|
|
AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
2019-06-03 10:51:51 -07:00
|
|
|
[&](Expr<SomeComplex> &&zx,
|
|
|
|
Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> {
|
2020-11-13 09:40:59 -08:00
|
|
|
if (opr == RelationalOperator::EQ ||
|
|
|
|
opr == RelationalOperator::NE) {
|
|
|
|
return PromoteAndRelate(opr, std::move(zx), std::move(zy));
|
|
|
|
} else {
|
2018-09-07 15:25:10 -07:00
|
|
|
messages.Say(
|
|
|
|
"COMPLEX data may be compared only for equality"_err_en_US);
|
2020-11-13 09:40:59 -08:00
|
|
|
return std::nullopt;
|
2018-09-07 15:25:10 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
|
2018-09-12 16:27:51 -07:00
|
|
|
return Relate(messages, opr, std::move(x),
|
|
|
|
AsGenericExpr(ConvertTo(zx, std::move(iy))));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
|
2018-09-12 16:27:51 -07:00
|
|
|
return Relate(messages, opr, std::move(x),
|
|
|
|
AsGenericExpr(ConvertTo(zx, std::move(ry))));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
|
2018-09-12 16:27:51 -07:00
|
|
|
return Relate(messages, opr,
|
|
|
|
AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
|
2018-09-12 16:27:51 -07:00
|
|
|
return Relate(messages, opr,
|
|
|
|
AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y));
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
[&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2019-06-03 10:51:51 -07:00
|
|
|
[&](auto &&cxk,
|
|
|
|
auto &&cyk) -> std::optional<Expr<LogicalResult>> {
|
2018-09-07 15:25:10 -07:00
|
|
|
using Ty = ResultType<decltype(cxk)>;
|
|
|
|
if constexpr (std::is_same_v<Ty, ResultType<decltype(cyk)>>) {
|
2019-06-03 10:51:51 -07:00
|
|
|
return PackageRelation(opr, std::move(cxk), std::move(cyk));
|
2018-09-07 15:25:10 -07:00
|
|
|
} else {
|
|
|
|
messages.Say(
|
|
|
|
"CHARACTER operands do not have same KIND"_err_en_US);
|
2019-08-01 11:41:05 -07:00
|
|
|
return std::nullopt;
|
2018-09-07 15:25:10 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
std::move(cx.u), std::move(cy.u));
|
|
|
|
},
|
|
|
|
// Default case
|
2019-08-01 11:41:05 -07:00
|
|
|
[&](auto &&, auto &&) {
|
2019-11-02 09:56:46 -07:00
|
|
|
DIE("invalid types for relational operator");
|
2019-08-01 11:41:05 -07:00
|
|
|
return std::optional<Expr<LogicalResult>>{};
|
2018-11-29 09:27:34 -08:00
|
|
|
},
|
|
|
|
},
|
2018-09-07 15:25:10 -07:00
|
|
|
std::move(x.u), std::move(y.u));
|
|
|
|
}
|
|
|
|
|
|
|
|
Expr<SomeLogical> BinaryLogicalOperation(
|
|
|
|
LogicalOperator opr, Expr<SomeLogical> &&x, Expr<SomeLogical> &&y) {
|
2019-11-06 15:54:26 -08:00
|
|
|
CHECK(opr != LogicalOperator::Not);
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2018-09-07 15:25:10 -07:00
|
|
|
[=](auto &&xy) {
|
|
|
|
using Ty = ResultType<decltype(xy[0])>;
|
2019-01-07 10:15:27 -08:00
|
|
|
return Expr<SomeLogical>{BinaryLogicalOperation<Ty::kind>(
|
|
|
|
opr, std::move(xy[0]), std::move(xy[1]))};
|
2018-09-07 15:25:10 -07:00
|
|
|
},
|
|
|
|
AsSameKindExprs(std::move(x), std::move(y)));
|
|
|
|
}
|
2019-01-22 16:30:32 -08:00
|
|
|
|
2020-03-27 14:17:25 -07:00
|
|
|
template <TypeCategory TO>
|
2019-01-22 16:30:32 -08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
|
|
|
|
static_assert(common::IsNumericTypeCategory(TO));
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2019-01-22 16:30:32 -08:00
|
|
|
[=](auto &&cx) -> std::optional<Expr<SomeType>> {
|
|
|
|
using cxType = std::decay_t<decltype(cx)>;
|
2019-05-03 11:29:15 -07:00
|
|
|
if constexpr (!common::HasMember<cxType, TypelessExpression>) {
|
2019-01-22 16:30:32 -08:00
|
|
|
if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) {
|
2019-06-03 10:51:51 -07:00
|
|
|
return Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))};
|
2019-01-22 16:30:32 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
std::move(x.u));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
|
|
|
const DynamicType &type, Expr<SomeType> &&x) {
|
2022-01-11 10:38:26 -08:00
|
|
|
if (type.IsTypelessIntrinsicArgument()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-05-13 09:33:18 -07:00
|
|
|
switch (type.category()) {
|
2019-01-22 16:30:32 -08:00
|
|
|
case TypeCategory::Integer:
|
2019-06-03 10:51:51 -07: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-13 09:33:18 -07:00
|
|
|
return ConvertToNumeric<TypeCategory::Integer>(type.kind(), std::move(x));
|
2024-12-18 07:02:37 -08:00
|
|
|
case TypeCategory::Unsigned:
|
|
|
|
if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
|
|
|
|
return Expr<SomeType>{
|
|
|
|
ConvertToKind<TypeCategory::Unsigned>(type.kind(), std::move(*boz))};
|
|
|
|
}
|
|
|
|
if (auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(x)}) {
|
|
|
|
return Expr<SomeType>{
|
|
|
|
ConvertToKind<TypeCategory::Unsigned>(type.kind(), std::move(*cx))};
|
|
|
|
}
|
|
|
|
break;
|
2019-01-22 16:30:32 -08:00
|
|
|
case TypeCategory::Real:
|
2019-07-17 14:26:35 -07:00
|
|
|
if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
|
|
|
|
return Expr<SomeType>{
|
|
|
|
ConvertToKind<TypeCategory::Real>(type.kind(), std::move(*boz))};
|
|
|
|
}
|
2019-05-13 09:33:18 -07:00
|
|
|
return ConvertToNumeric<TypeCategory::Real>(type.kind(), std::move(x));
|
2019-01-22 16:30:32 -08:00
|
|
|
case TypeCategory::Complex:
|
2019-05-13 09:33:18 -07:00
|
|
|
return ConvertToNumeric<TypeCategory::Complex>(type.kind(), std::move(x));
|
2019-01-22 16:30:32 -08:00
|
|
|
case TypeCategory::Character:
|
2019-02-19 15:38:55 -08:00
|
|
|
if (auto *cx{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
|
2019-02-27 15:51:03 -08:00
|
|
|
auto converted{
|
2019-05-13 09:33:18 -07:00
|
|
|
ConvertToKind<TypeCategory::Character>(type.kind(), std::move(*cx))};
|
2021-06-02 17:13:55 -07:00
|
|
|
if (auto length{type.GetCharLength()}) {
|
2022-03-23 14:05:50 -07:00
|
|
|
converted = common::visit(
|
2021-06-02 17:13:55 -07:00
|
|
|
[&](auto &&x) {
|
2023-09-13 16:34:23 -07:00
|
|
|
using CharacterType = ResultType<decltype(x)>;
|
2021-06-02 17:13:55 -07:00
|
|
|
return Expr<SomeCharacter>{
|
|
|
|
Expr<CharacterType>{SetLength<CharacterType::kind>{
|
|
|
|
std::move(x), std::move(*length)}}};
|
|
|
|
},
|
|
|
|
std::move(converted.u));
|
2019-02-27 15:51:03 -08:00
|
|
|
}
|
|
|
|
return Expr<SomeType>{std::move(converted)};
|
2019-01-22 16:30:32 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TypeCategory::Logical:
|
|
|
|
if (auto *cx{UnwrapExpr<Expr<SomeLogical>>(x)}) {
|
|
|
|
return Expr<SomeType>{
|
2019-05-13 09:33:18 -07:00
|
|
|
ConvertToKind<TypeCategory::Logical>(type.kind(), std::move(*cx))};
|
2019-01-22 16:30:32 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TypeCategory::Derived:
|
|
|
|
if (auto fromType{x.GetType()}) {
|
2022-02-08 11:56:22 -08:00
|
|
|
if (type.IsTkCompatibleWith(*fromType)) {
|
|
|
|
// "x" could be assigned or passed to "type", or appear in a
|
|
|
|
// structure constructor as a value for a component with "type"
|
2019-01-22 16:30:32 -08:00
|
|
|
return std::move(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-05-13 09:33:18 -07:00
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
|
|
|
const DynamicType &to, std::optional<Expr<SomeType>> &&x) {
|
2019-11-09 09:29:31 -08:00
|
|
|
if (x) {
|
2019-05-13 09:33:18 -07:00
|
|
|
return ConvertToType(to, std::move(*x));
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-19 15:38:55 -08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
2019-10-22 16:53:29 -07:00
|
|
|
const Symbol &symbol, Expr<SomeType> &&x) {
|
2019-05-03 11:29:15 -07:00
|
|
|
if (auto symType{DynamicType::From(symbol)}) {
|
2019-02-19 15:38:55 -08:00
|
|
|
return ConvertToType(*symType, std::move(x));
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-01-22 16:30:32 -08:00
|
|
|
std::optional<Expr<SomeType>> ConvertToType(
|
2019-10-22 16:53:29 -07:00
|
|
|
const Symbol &to, std::optional<Expr<SomeType>> &&x) {
|
2019-11-09 09:29:31 -08:00
|
|
|
if (x) {
|
2019-05-13 09:33:18 -07:00
|
|
|
return ConvertToType(to, std::move(*x));
|
2019-01-22 16:30:32 -08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
2019-05-03 11:29:15 -07: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-12 15:36:45 -08:00
|
|
|
bool IsAssumedRank(const Symbol &original) {
|
2021-09-13 13:45:30 -07:00
|
|
|
if (const auto *assoc{original.detailsIf<semantics::AssocEntityDetails>()}) {
|
|
|
|
if (assoc->rank()) {
|
2023-09-13 16:13:31 -07:00
|
|
|
return false; // in RANK(n) or RANK(*)
|
|
|
|
} else if (assoc->IsAssumedRank()) {
|
|
|
|
return true; // RANK DEFAULT
|
2021-09-13 13:45:30 -07: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-12 15:36:45 -08:00
|
|
|
const Symbol &symbol{semantics::ResolveAssociations(original)};
|
2023-09-13 16:13:31 -07:00
|
|
|
const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()};
|
|
|
|
return object && object->IsAssumedRank();
|
2019-05-03 11:29:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsAssumedRank(const ActualArgument &arg) {
|
2019-05-21 16:58:46 -07:00
|
|
|
if (const auto *expr{arg.UnwrapExpr()}) {
|
2019-05-03 11:29:15 -07:00
|
|
|
return IsAssumedRank(*expr);
|
|
|
|
} else {
|
2019-10-22 16:53:29 -07:00
|
|
|
const Symbol *assumedTypeDummy{arg.GetAssumedTypeDummy()};
|
2019-11-09 09:29:31 -08:00
|
|
|
CHECK(assumedTypeDummy);
|
2019-05-03 11:29:15 -07:00
|
|
|
return IsAssumedRank(*assumedTypeDummy);
|
|
|
|
}
|
|
|
|
}
|
2019-07-23 10:55:56 -07:00
|
|
|
|
2025-01-08 13:16:56 -08:00
|
|
|
int GetCorank(const ActualArgument &arg) {
|
2021-11-18 11:48:42 -08:00
|
|
|
const auto *expr{arg.UnwrapExpr()};
|
2025-01-08 13:16:56 -08:00
|
|
|
return GetCorank(*expr);
|
2021-09-16 17:21:40 -07:00
|
|
|
}
|
|
|
|
|
2024-06-03 12:58:39 -07:00
|
|
|
bool IsProcedureDesignator(const Expr<SomeType> &expr) {
|
2019-11-07 16:01:38 -08:00
|
|
|
return std::holds_alternative<ProcedureDesignator>(expr.u);
|
|
|
|
}
|
2024-06-03 12:58:39 -07:00
|
|
|
bool IsFunctionDesignator(const Expr<SomeType> &expr) {
|
2020-08-18 10:47:52 -07:00
|
|
|
const auto *designator{std::get_if<ProcedureDesignator>(&expr.u)};
|
|
|
|
return designator && designator->GetType().has_value();
|
|
|
|
}
|
2019-11-07 16:01:38 -08:00
|
|
|
|
2023-09-18 08:22:18 -07:00
|
|
|
bool IsPointer(const Expr<SomeType> &expr) {
|
|
|
|
return IsObjectPointer(expr) || IsProcedurePointer(expr);
|
|
|
|
}
|
|
|
|
|
2023-01-11 14:31:49 -08:00
|
|
|
bool IsProcedurePointer(const Expr<SomeType> &expr) {
|
2025-03-03 14:46:35 -08:00
|
|
|
if (IsNullProcedurePointer(&expr)) {
|
2023-09-18 08:22:18 -07:00
|
|
|
return true;
|
|
|
|
} else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
|
|
|
|
if (const Symbol * proc{funcRef->proc().GetSymbol()}) {
|
|
|
|
const Symbol *result{FindFunctionResult(*proc)};
|
|
|
|
return result && IsProcedurePointer(*result);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) {
|
|
|
|
return IsProcedurePointer(proc->GetSymbol());
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2023-01-11 14:31:49 -08:00
|
|
|
}
|
|
|
|
|
2024-06-03 12:58:39 -07:00
|
|
|
bool IsProcedure(const Expr<SomeType> &expr) {
|
|
|
|
return IsProcedureDesignator(expr) || IsProcedurePointer(expr);
|
|
|
|
}
|
|
|
|
|
2021-03-12 13:51:33 -08:00
|
|
|
bool IsProcedurePointerTarget(const Expr<SomeType> &expr) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(common::visitors{
|
|
|
|
[](const NullPointer &) { return true; },
|
|
|
|
[](const ProcedureDesignator &) { return true; },
|
|
|
|
[](const ProcedureRef &) { return true; },
|
|
|
|
[&](const auto &) {
|
|
|
|
const Symbol *last{GetLastSymbol(expr)};
|
|
|
|
return last && IsProcedurePointer(*last);
|
|
|
|
},
|
|
|
|
},
|
2019-10-08 15:21:09 -07:00
|
|
|
expr.u);
|
|
|
|
}
|
|
|
|
|
2023-09-18 08:22:18 -07:00
|
|
|
bool IsObjectPointer(const Expr<SomeType> &expr) {
|
2025-03-03 14:46:35 -08:00
|
|
|
if (IsNullObjectPointer(&expr)) {
|
2021-01-19 17:14:41 -08:00
|
|
|
return true;
|
2021-03-12 13:51:33 -08:00
|
|
|
} else if (IsProcedurePointerTarget(expr)) {
|
2021-01-19 17:14:41 -08:00
|
|
|
return false;
|
2021-03-12 13:51:33 -08:00
|
|
|
} else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
|
|
|
|
return IsVariable(*funcRef);
|
2022-03-11 09:22:47 +01:00
|
|
|
} else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) {
|
2021-01-19 17:14:41 -08:00
|
|
|
return IsPointer(symbol->GetUltimate());
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-21 12:02:15 -07:00
|
|
|
// IsNullPointer() & variations
|
2021-10-14 10:32:59 -07:00
|
|
|
|
2022-08-25 10:27:32 -07:00
|
|
|
template <bool IS_PROC_PTR> struct IsNullPointerHelper {
|
2021-09-07 12:17:31 -07:00
|
|
|
template <typename A> bool operator()(const A &) const { return false; }
|
2022-08-25 10:27:32 -07:00
|
|
|
bool operator()(const ProcedureRef &call) const {
|
|
|
|
if constexpr (IS_PROC_PTR) {
|
|
|
|
const auto *intrinsic{call.proc().GetSpecificIntrinsic()};
|
|
|
|
return intrinsic &&
|
|
|
|
intrinsic->characteristics.value().attrs.test(
|
|
|
|
characteristics::Procedure::Attr::NullPointer);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2021-09-07 12:17:31 -07:00
|
|
|
template <typename T> bool operator()(const FunctionRef<T> &call) const {
|
2022-08-25 10:27:32 -07:00
|
|
|
if constexpr (IS_PROC_PTR) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
const auto *intrinsic{call.proc().GetSpecificIntrinsic()};
|
|
|
|
return intrinsic &&
|
|
|
|
intrinsic->characteristics.value().attrs.test(
|
|
|
|
characteristics::Procedure::Attr::NullPointer);
|
|
|
|
}
|
2019-10-22 10:34:05 -07:00
|
|
|
}
|
2022-09-21 12:02:15 -07:00
|
|
|
template <typename T> bool operator()(const Designator<T> &x) const {
|
|
|
|
if (const auto *component{std::get_if<Component>(&x.u)}) {
|
|
|
|
if (const auto *baseSym{std::get_if<SymbolRef>(&component->base().u)}) {
|
|
|
|
const Symbol &base{**baseSym};
|
|
|
|
if (const auto *object{
|
|
|
|
base.detailsIf<semantics::ObjectEntityDetails>()}) {
|
|
|
|
// TODO: nested component and array references
|
|
|
|
if (IsNamedConstant(base) && object->init()) {
|
|
|
|
if (auto structCons{
|
|
|
|
GetScalarConstantValue<SomeDerived>(*object->init())}) {
|
|
|
|
auto iter{structCons->values().find(component->GetLastSymbol())};
|
|
|
|
if (iter != structCons->values().end()) {
|
|
|
|
return (*this)(iter->second.value());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-10-22 10:34:05 -07:00
|
|
|
bool operator()(const NullPointer &) const { return true; }
|
2021-09-07 12:17:31 -07: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 {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(*this, x.u);
|
2021-09-07 12:17:31 -07:00
|
|
|
}
|
2019-10-22 10:34:05 -07:00
|
|
|
};
|
2021-09-07 12:17:31 -07:00
|
|
|
|
2025-03-03 14:46:35 -08:00
|
|
|
bool IsNullObjectPointer(const Expr<SomeType> *expr) {
|
|
|
|
return expr && IsNullPointerHelper<false>{}(*expr);
|
2022-08-25 10:27:32 -07:00
|
|
|
}
|
2022-09-21 12:02:15 -07:00
|
|
|
|
2025-03-03 14:46:35 -08:00
|
|
|
bool IsNullProcedurePointer(const Expr<SomeType> *expr) {
|
|
|
|
return expr && IsNullPointerHelper<true>{}(*expr);
|
2022-08-25 10:27:32 -07:00
|
|
|
}
|
2022-09-21 12:02:15 -07:00
|
|
|
|
2025-03-03 14:46:35 -08:00
|
|
|
bool IsNullPointer(const Expr<SomeType> *expr) {
|
2022-08-25 10:27:32 -07:00
|
|
|
return IsNullObjectPointer(expr) || IsNullProcedurePointer(expr);
|
2019-10-08 15:21:09 -07:00
|
|
|
}
|
|
|
|
|
2022-09-21 12:02:15 -07:00
|
|
|
bool IsBareNullPointer(const Expr<SomeType> *expr) {
|
|
|
|
return expr && std::holds_alternative<NullPointer>(expr->u);
|
|
|
|
}
|
|
|
|
|
2025-03-03 14:46:35 -08:00
|
|
|
struct IsNullAllocatableHelper {
|
|
|
|
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()};
|
|
|
|
return intrinsic &&
|
|
|
|
intrinsic->characteristics.value().attrs.test(
|
|
|
|
characteristics::Procedure::Attr::NullAllocatable);
|
|
|
|
}
|
|
|
|
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 common::visit(*this, x.u);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool IsNullAllocatable(const Expr<SomeType> *x) {
|
|
|
|
return x && IsNullAllocatableHelper{}(*x);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsNullPointerOrAllocatable(const Expr<SomeType> *x) {
|
|
|
|
return IsNullPointer(x) || IsNullAllocatable(x);
|
|
|
|
}
|
|
|
|
|
2019-11-01 13:08:16 -07:00
|
|
|
// GetSymbolVector()
|
|
|
|
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
|
2020-01-15 13:43:05 -08:00
|
|
|
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
|
2023-09-18 08:22:18 -07:00
|
|
|
if (IsVariable(details->expr()) && !UnwrapProcedureRef(*details->expr())) {
|
2022-10-04 11:10:59 -07:00
|
|
|
// associate(x => variable that is not a pointer returned by a function)
|
|
|
|
return (*this)(details->expr());
|
|
|
|
}
|
2020-01-15 13:43:05 -08:00
|
|
|
}
|
2022-10-04 11:10:59 -07:00
|
|
|
return {x.GetUltimate()};
|
2019-07-23 10:55:56 -07:00
|
|
|
}
|
2019-11-01 13:08:16 -07: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-23 10:55:56 -07:00
|
|
|
}
|
|
|
|
|
2019-09-19 12:19:17 -07:00
|
|
|
struct CollectSymbolsHelper
|
2021-03-18 10:26:23 -07:00
|
|
|
: public SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet> {
|
|
|
|
using Base = SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet>;
|
2019-09-19 12:19:17 -07:00
|
|
|
CollectSymbolsHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
2021-03-18 10:26:23 -07:00
|
|
|
semantics::UnorderedSymbolSet operator()(const Symbol &symbol) const {
|
2019-10-22 16:53:29 -07:00
|
|
|
return {symbol};
|
2019-09-19 12:19:17 -07:00
|
|
|
}
|
|
|
|
};
|
2021-03-18 10:26:23 -07:00
|
|
|
template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &x) {
|
2019-09-19 12:19:17 -07:00
|
|
|
return CollectSymbolsHelper{}(x);
|
|
|
|
}
|
2021-03-18 10:26:23 -07:00
|
|
|
template semantics::UnorderedSymbolSet CollectSymbols(const Expr<SomeType> &);
|
|
|
|
template semantics::UnorderedSymbolSet CollectSymbols(
|
|
|
|
const Expr<SomeInteger> &);
|
|
|
|
template semantics::UnorderedSymbolSet CollectSymbols(
|
|
|
|
const Expr<SubscriptInteger> &);
|
2019-09-19 12:19:17 -07:00
|
|
|
|
2024-07-11 09:36:35 -07:00
|
|
|
struct CollectCudaSymbolsHelper : public SetTraverse<CollectCudaSymbolsHelper,
|
|
|
|
semantics::UnorderedSymbolSet> {
|
|
|
|
using Base =
|
|
|
|
SetTraverse<CollectCudaSymbolsHelper, semantics::UnorderedSymbolSet>;
|
|
|
|
CollectCudaSymbolsHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
|
|
|
semantics::UnorderedSymbolSet operator()(const Symbol &symbol) const {
|
|
|
|
return {symbol};
|
|
|
|
}
|
|
|
|
// Overload some of the operator() to filter out the symbols that are not
|
|
|
|
// of interest for CUDA data transfer logic.
|
2024-09-10 14:07:26 -07:00
|
|
|
semantics::UnorderedSymbolSet operator()(const DescriptorInquiry &) const {
|
|
|
|
return {};
|
|
|
|
}
|
2024-07-11 09:36:35 -07:00
|
|
|
semantics::UnorderedSymbolSet operator()(const Subscript &) const {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
semantics::UnorderedSymbolSet operator()(const ProcedureRef &) const {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
template <typename A>
|
|
|
|
semantics::UnorderedSymbolSet CollectCudaSymbols(const A &x) {
|
|
|
|
return CollectCudaSymbolsHelper{}(x);
|
|
|
|
}
|
|
|
|
template semantics::UnorderedSymbolSet CollectCudaSymbols(
|
|
|
|
const Expr<SomeType> &);
|
|
|
|
template semantics::UnorderedSymbolSet CollectCudaSymbols(
|
|
|
|
const Expr<SomeInteger> &);
|
|
|
|
template semantics::UnorderedSymbolSet CollectCudaSymbols(
|
|
|
|
const Expr<SubscriptInteger> &);
|
|
|
|
|
2019-10-10 13:09:35 -07:00
|
|
|
// HasVectorSubscript()
|
2024-03-06 14:29:27 +01:00
|
|
|
struct HasVectorSubscriptHelper
|
|
|
|
: public AnyTraverse<HasVectorSubscriptHelper, bool,
|
|
|
|
/*TraverseAssocEntityDetails=*/false> {
|
|
|
|
using Base = AnyTraverse<HasVectorSubscriptHelper, bool, false>;
|
2019-10-10 13:09:35 -07:00
|
|
|
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-27 14:17:25 -07:00
|
|
|
return false; // don't descend into function call arguments
|
2019-10-10 13:09:35 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool HasVectorSubscript(const Expr<SomeType> &expr) {
|
|
|
|
return HasVectorSubscriptHelper{}(expr);
|
|
|
|
}
|
2019-11-01 13:08:16 -07:00
|
|
|
|
2024-11-26 17:04:00 -08:00
|
|
|
// HasConstant()
|
|
|
|
struct HasConstantHelper : public AnyTraverse<HasConstantHelper, bool,
|
|
|
|
/*TraverseAssocEntityDetails=*/false> {
|
|
|
|
using Base = AnyTraverse<HasConstantHelper, bool, false>;
|
|
|
|
HasConstantHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
|
|
|
template <typename T> bool operator()(const Constant<T> &) const {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Only look for constant not in subscript.
|
|
|
|
bool operator()(const Subscript &) const { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
bool HasConstant(const Expr<SomeType> &expr) {
|
|
|
|
return HasConstantHelper{}(expr);
|
|
|
|
}
|
|
|
|
|
2019-11-01 13:08:16 -07:00
|
|
|
parser::Message *AttachDeclaration(
|
2019-11-22 13: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 07:22:52 -07:00
|
|
|
"Procedure '%s' of type '%s' is bound to '%s'"_en_US, symbol.name(),
|
|
|
|
symbol.owner().GetName().value(), binding->symbol().name());
|
2019-11-22 13:20:58 -08:00
|
|
|
return &message;
|
2019-11-01 13:08:16 -07:00
|
|
|
}
|
2019-11-22 13: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-29 16:39:13 -07:00
|
|
|
unhosted->name(), GetUsedModule(*use).name());
|
2019-11-22 13:20:58 -08:00
|
|
|
} else {
|
|
|
|
message.Attach(
|
|
|
|
unhosted->name(), "Declaration of '%s'"_en_US, unhosted->name());
|
2019-11-01 13:08:16 -07:00
|
|
|
}
|
2019-11-12 15:43:09 -08:00
|
|
|
return &message;
|
|
|
|
}
|
|
|
|
|
|
|
|
parser::Message *AttachDeclaration(
|
2019-11-22 13: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-07 12:08:58 -08:00
|
|
|
return message ? AttachDeclaration(*message, symbol) : nullptr;
|
2019-11-01 13:08:16 -07:00
|
|
|
}
|
2019-11-15 14:26:10 -08:00
|
|
|
|
|
|
|
class FindImpureCallHelper
|
2024-03-06 14:29:27 +01:00
|
|
|
: public AnyTraverse<FindImpureCallHelper, std::optional<std::string>,
|
|
|
|
/*TraverseAssocEntityDetails=*/false> {
|
2019-11-15 14:26:10 -08:00
|
|
|
using Result = std::optional<std::string>;
|
2024-03-06 14:29:27 +01:00
|
|
|
using Base = AnyTraverse<FindImpureCallHelper, Result, false>;
|
2019-11-15 14:26:10 -08:00
|
|
|
|
|
|
|
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-07 12:08:58 -08:00
|
|
|
explicit FindImpureCallHelper(FoldingContext &c) : Base{*this}, context_{c} {}
|
2019-11-15 14:26:10 -08:00
|
|
|
using Base::operator();
|
|
|
|
Result operator()(const ProcedureRef &call) const {
|
2024-04-22 15:21:45 -07:00
|
|
|
if (auto chars{characteristics::Procedure::Characterize(
|
|
|
|
call.proc(), context_, /*emitError=*/false)}) {
|
2019-11-15 14:26:10 -08:00
|
|
|
if (chars->attrs.test(characteristics::Procedure::Attr::Pure)) {
|
2020-01-25 08:15:17 -08:00
|
|
|
return (*this)(call.arguments());
|
2019-11-15 14: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-07 12:08:58 -08:00
|
|
|
FoldingContext &context_;
|
2019-11-15 14: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-07 12:08:58 -08:00
|
|
|
FoldingContext &context, const Expr<SomeType> &expr) {
|
|
|
|
return FindImpureCallHelper{context}(expr);
|
2019-11-15 14:26:10 -08:00
|
|
|
}
|
2020-02-18 17: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-07 12:08:58 -08:00
|
|
|
FoldingContext &context, const ProcedureRef &proc) {
|
|
|
|
return FindImpureCallHelper{context}(proc);
|
2020-02-18 17:14:24 -08:00
|
|
|
}
|
2019-11-15 14:26:10 -08:00
|
|
|
|
2020-09-25 09:03:17 -07:00
|
|
|
// 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
|
2022-07-08 13:38:31 -07:00
|
|
|
// and the content of the "whyNotCompatible" string.
|
2020-09-25 09:03:17 -07:00
|
|
|
std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
|
|
|
|
const std::optional<characteristics::Procedure> &lhsProcedure,
|
2022-07-08 13:38:31 -07:00
|
|
|
const characteristics::Procedure *rhsProcedure,
|
2023-11-30 12:22:04 -08:00
|
|
|
const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible,
|
2024-03-01 15:56:40 -08:00
|
|
|
std::optional<std::string> &warning, bool ignoreImplicitVsExplicit) {
|
2020-09-25 09:03:17 -07:00
|
|
|
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;
|
2022-10-12 12:59:48 -07:00
|
|
|
} else if (!isCall && lhsProcedure->functionResult &&
|
|
|
|
rhsProcedure->functionResult &&
|
|
|
|
!lhsProcedure->functionResult->IsCompatibleWith(
|
|
|
|
*rhsProcedure->functionResult, &whyNotCompatible)) {
|
|
|
|
msg =
|
|
|
|
"Function %s associated with incompatible function designator '%s': %s"_err_en_US;
|
2024-03-01 15:56:40 -08:00
|
|
|
} else if (lhsProcedure->IsCompatibleWith(*rhsProcedure,
|
|
|
|
ignoreImplicitVsExplicit, &whyNotCompatible, specificIntrinsic,
|
|
|
|
&warning)) {
|
2020-09-25 09:03:17 -07:00
|
|
|
// OK
|
|
|
|
} else if (isCall) {
|
|
|
|
msg = "Procedure %s associated with result of reference to function '%s'"
|
2022-07-08 13:38:31 -07:00
|
|
|
" that is an incompatible procedure pointer: %s"_err_en_US;
|
2020-09-25 09:03:17 -07:00
|
|
|
} else if (lhsProcedure->IsPure() && !rhsProcedure->IsPure()) {
|
|
|
|
msg = "PURE procedure %s may not be associated with non-PURE"
|
|
|
|
" procedure designator '%s'"_err_en_US;
|
2022-07-08 13:38:31 -07:00
|
|
|
} else if (lhsProcedure->IsFunction() && rhsProcedure->IsSubroutine()) {
|
2020-09-25 09:03:17 -07:00
|
|
|
msg = "Function %s may not be associated with subroutine"
|
|
|
|
" designator '%s'"_err_en_US;
|
2022-07-08 13:38:31 -07:00
|
|
|
} else if (lhsProcedure->IsSubroutine() && rhsProcedure->IsFunction()) {
|
2020-09-25 09:03:17 -07:00
|
|
|
msg = "Subroutine %s may not be associated with function"
|
|
|
|
" designator '%s'"_err_en_US;
|
|
|
|
} else if (lhsProcedure->HasExplicitInterface() &&
|
|
|
|
!rhsProcedure->HasExplicitInterface()) {
|
2021-11-11 10:46:26 -08:00
|
|
|
// Section 10.2.2.4, paragraph 3 prohibits associating a procedure pointer
|
2022-07-22 11:48:07 -07:00
|
|
|
// that has an explicit interface with a procedure whose characteristics
|
|
|
|
// don't match. That's the case if the target procedure has an implicit
|
2022-02-09 19:46:34 -08:00
|
|
|
// 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-25 09:03:17 -07:00
|
|
|
} else if (!lhsProcedure->HasExplicitInterface() &&
|
|
|
|
rhsProcedure->HasExplicitInterface()) {
|
2022-02-09 19:46:34 -08:00
|
|
|
// OK if the target can be called via an implicit interface
|
2022-07-22 11:48:07 -07:00
|
|
|
if (!rhsProcedure->CanBeCalledViaImplicitInterface() &&
|
|
|
|
!specificIntrinsic) {
|
2021-11-11 10:46:26 -08:00
|
|
|
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-25 09:03:17 -07:00
|
|
|
} else {
|
|
|
|
msg = "Procedure %s associated with incompatible procedure"
|
2022-07-08 13:38:31 -07:00
|
|
|
" designator '%s': %s"_err_en_US;
|
2020-09-25 09:03:17 -07:00
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2021-01-15 16: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) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
|
|
|
[](const auto &y) { return GetLastPointerSymbol(y); }, x.u);
|
2021-01-15 16:59:52 -08:00
|
|
|
}
|
|
|
|
|
2022-01-04 09:25:40 -08:00
|
|
|
template <TypeCategory TO, TypeCategory FROM>
|
|
|
|
static std::optional<Expr<SomeType>> DataConstantConversionHelper(
|
|
|
|
FoldingContext &context, const DynamicType &toType,
|
|
|
|
const Expr<SomeType> &expr) {
|
2025-04-04 08:42:38 -07:00
|
|
|
if (!common::IsValidKindOfIntrinsicType(FROM, toType.kind())) {
|
2025-01-14 10:40:39 -08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2022-01-04 09:25:40 -08:00
|
|
|
DynamicType sizedType{FROM, toType.kind()};
|
|
|
|
if (auto sized{
|
|
|
|
Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) {
|
|
|
|
if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2022-01-04 09:25:40 -08:00
|
|
|
[](const auto &w) -> std::optional<Expr<SomeType>> {
|
2023-09-13 16:34:23 -07:00
|
|
|
using FromType = ResultType<decltype(w)>;
|
2022-01-04 09:25:40 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-09-18 08:22:18 -07:00
|
|
|
bool IsAllocatableOrPointerObject(const Expr<SomeType> &expr) {
|
2022-02-02 19:52:28 +01:00
|
|
|
const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
|
2023-08-21 12:21:49 -07:00
|
|
|
return (sym &&
|
|
|
|
semantics::IsAllocatableOrObjectPointer(&sym->GetUltimate())) ||
|
2025-03-03 14:46:35 -08:00
|
|
|
evaluate::IsObjectPointer(expr) || evaluate::IsNullAllocatable(&expr);
|
2022-02-02 19:52:28 +01:00
|
|
|
}
|
|
|
|
|
2022-04-01 22:31:23 +02:00
|
|
|
bool IsAllocatableDesignator(const Expr<SomeType> &expr) {
|
|
|
|
// Allocatable sub-objects are not themselves allocatable (9.5.3.1 NOTE 2).
|
|
|
|
if (const semantics::Symbol *
|
|
|
|
sym{UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) {
|
2023-02-21 15:16:16 +00:00
|
|
|
return semantics::IsAllocatable(sym->GetUltimate());
|
2022-04-01 22:31:23 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-09-18 08:22:18 -07:00
|
|
|
bool MayBePassedAsAbsentOptional(const Expr<SomeType> &expr) {
|
2022-02-02 19:52:28 +01:00
|
|
|
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)) ||
|
2023-09-18 08:22:18 -07:00
|
|
|
IsAllocatableOrPointerObject(expr);
|
2022-02-02 19:52:28 +01:00
|
|
|
}
|
|
|
|
|
2022-05-19 09:55:29 -07:00
|
|
|
std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
|
|
|
|
const Expr<SomeType> &expr, const DynamicType &type) {
|
|
|
|
if (std::optional<std::string> chValue{GetScalarConstantValue<Ascii>(expr)}) {
|
|
|
|
// Pad on the right with spaces when short, truncate the right if long.
|
|
|
|
auto bytes{static_cast<std::size_t>(
|
|
|
|
ToInt64(type.MeasureSizeInBytes(context, false)).value())};
|
|
|
|
BOZLiteralConstant bits{0};
|
|
|
|
for (std::size_t j{0}; j < bytes; ++j) {
|
2024-08-17 16:53:11 -04:00
|
|
|
auto idx{isHostLittleEndian ? j : bytes - j - 1};
|
|
|
|
char ch{idx >= chValue->size() ? ' ' : chValue->at(idx)};
|
2022-05-19 09:55:29 -07:00
|
|
|
BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
|
|
|
|
bits = bits.IOR(chBOZ.SHIFTL(8 * j));
|
|
|
|
}
|
|
|
|
return ConvertToType(type, Expr<SomeType>{bits});
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-30 12:22:04 -08:00
|
|
|
// Extracts a whole symbol being used as a bound of a dummy argument,
|
|
|
|
// possibly wrapped with parentheses or MAX(0, ...).
|
2024-10-15 14:22:48 -07:00
|
|
|
// Works with any integer expression.
|
|
|
|
template <typename T> const Symbol *GetBoundSymbol(const Expr<T> &);
|
2023-11-30 12:22:04 -08:00
|
|
|
template <int KIND>
|
2024-10-15 14:22:48 -07:00
|
|
|
const Symbol *GetBoundSymbol(
|
2023-11-30 12:22:04 -08:00
|
|
|
const Expr<Type<TypeCategory::Integer, KIND>> &expr) {
|
|
|
|
using T = Type<TypeCategory::Integer, KIND>;
|
|
|
|
return common::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const Extremum<T> &max) -> const Symbol * {
|
|
|
|
if (max.ordering == Ordering::Greater) {
|
|
|
|
if (auto zero{ToInt64(max.left())}; zero && *zero == 0) {
|
|
|
|
return GetBoundSymbol(max.right());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
},
|
|
|
|
[](const Parentheses<T> &x) { return GetBoundSymbol(x.left()); },
|
|
|
|
[](const Designator<T> &x) -> const Symbol * {
|
|
|
|
if (const auto *ref{std::get_if<SymbolRef>(&x.u)}) {
|
|
|
|
return &**ref;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
},
|
|
|
|
[](const Convert<T, TypeCategory::Integer> &x) {
|
|
|
|
return common::visit(
|
|
|
|
[](const auto &y) -> const Symbol * {
|
|
|
|
using yType = std::decay_t<decltype(y)>;
|
|
|
|
using yResult = typename yType::Result;
|
|
|
|
if constexpr (yResult::kind <= KIND) {
|
|
|
|
return GetBoundSymbol(y);
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
x.left().u);
|
|
|
|
},
|
|
|
|
[](const auto &) -> const Symbol * { return nullptr; },
|
|
|
|
},
|
|
|
|
expr.u);
|
|
|
|
}
|
2024-10-15 14:22:48 -07:00
|
|
|
template <>
|
|
|
|
const Symbol *GetBoundSymbol<SomeInteger>(const Expr<SomeInteger> &expr) {
|
|
|
|
return common::visit(
|
|
|
|
[](const auto &kindExpr) { return GetBoundSymbol(kindExpr); }, expr.u);
|
|
|
|
}
|
2023-11-30 12:22:04 -08:00
|
|
|
|
2024-10-15 14:22:48 -07:00
|
|
|
template <typename T>
|
2023-11-30 12:22:04 -08:00
|
|
|
std::optional<bool> AreEquivalentInInterface(
|
2024-10-15 14:22:48 -07:00
|
|
|
const Expr<T> &x, const Expr<T> &y) {
|
2023-11-30 12:22:04 -08:00
|
|
|
auto xVal{ToInt64(x)};
|
|
|
|
auto yVal{ToInt64(y)};
|
|
|
|
if (xVal && yVal) {
|
|
|
|
return *xVal == *yVal;
|
|
|
|
} else if (xVal || yVal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const Symbol *xSym{GetBoundSymbol(x)};
|
|
|
|
const Symbol *ySym{GetBoundSymbol(y)};
|
|
|
|
if (xSym && ySym) {
|
|
|
|
if (&xSym->GetUltimate() == &ySym->GetUltimate()) {
|
|
|
|
return true; // USE/host associated same symbol
|
|
|
|
}
|
|
|
|
auto xNum{semantics::GetDummyArgumentNumber(xSym)};
|
|
|
|
auto yNum{semantics::GetDummyArgumentNumber(ySym)};
|
|
|
|
if (xNum && yNum) {
|
|
|
|
if (*xNum == *yNum) {
|
|
|
|
auto xType{DynamicType::From(*xSym)};
|
|
|
|
auto yType{DynamicType::From(*ySym)};
|
|
|
|
return xType && yType && xType->IsEquivalentTo(*yType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} else if (xSym || ySym) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Neither expression is an integer constant or a whole symbol.
|
|
|
|
if (x == y) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return std::nullopt; // not sure
|
|
|
|
}
|
|
|
|
}
|
2024-10-15 14:22:48 -07:00
|
|
|
template std::optional<bool> AreEquivalentInInterface<SubscriptInteger>(
|
|
|
|
const Expr<SubscriptInteger> &, const Expr<SubscriptInteger> &);
|
|
|
|
template std::optional<bool> AreEquivalentInInterface<SomeInteger>(
|
|
|
|
const Expr<SomeInteger> &, const Expr<SomeInteger> &);
|
2023-11-30 12:22:04 -08:00
|
|
|
|
2024-01-15 11:57:37 -08:00
|
|
|
bool CheckForCoindexedObject(parser::ContextualMessages &messages,
|
|
|
|
const std::optional<ActualArgument> &arg, const std::string &procName,
|
|
|
|
const std::string &argName) {
|
|
|
|
if (arg && ExtractCoarrayRef(arg->UnwrapExpr())) {
|
|
|
|
messages.Say(arg->sourceLocation(),
|
|
|
|
"'%s' argument to '%s' may not be a coindexed object"_err_en_US,
|
|
|
|
argName, procName);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 14:17:25 -07: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-29 16:39:13 -07:00
|
|
|
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
2025-02-27 14:32:12 -08:00
|
|
|
const Symbol &ResolveAssociations(
|
|
|
|
const Symbol &original, bool stopAtTypeGuard) {
|
[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-12 15:36:45 -08:00
|
|
|
const Symbol &symbol{original.GetUltimate()};
|
|
|
|
if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
|
2025-02-27 14:32:12 -08:00
|
|
|
if (!details->rank() /* not RANK(n) or RANK(*) */ &&
|
|
|
|
!(stopAtTypeGuard && details->isTypeGuard())) {
|
2023-02-02 14:26:01 -08:00
|
|
|
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-29 16:39:13 -07: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-12 15: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-29 16:39:13 -07: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-12 15: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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2025-02-27 14:32:12 -08:00
|
|
|
const Symbol &GetAssociationRoot(const Symbol &original, bool stopAtTypeGuard) {
|
|
|
|
const Symbol &symbol{ResolveAssociations(original, stopAtTypeGuard)};
|
[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-12 15:36:45 -08:00
|
|
|
if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
|
|
|
|
if (const Symbol * root{GetAssociatedVariable(*details)}) {
|
|
|
|
return *root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbol;
|
2020-12-09 08:38:59 -08:00
|
|
|
}
|
|
|
|
|
2021-11-11 12: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-12 15:36:45 -08:00
|
|
|
bool IsVariableName(const Symbol &original) {
|
2023-02-09 10:00:12 -08:00
|
|
|
const Symbol &ultimate{original.GetUltimate()};
|
|
|
|
return !IsNamedConstant(ultimate) &&
|
|
|
|
(ultimate.has<ObjectEntityDetails>() ||
|
|
|
|
ultimate.has<AssocEntityDetails>());
|
[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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
|
2023-06-22 09:25:03 -07:00
|
|
|
static bool IsPureProcedureImpl(
|
|
|
|
const Symbol &original, semantics::UnorderedSymbolSet &set) {
|
2021-11-11 12:36:15 -08:00
|
|
|
// An ENTRY is pure if its containing subprogram is
|
|
|
|
const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
|
2023-06-22 09:25:03 -07:00
|
|
|
if (set.find(symbol) != set.end()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
set.emplace(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-29 16:39:13 -07:00
|
|
|
if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
|
2022-12-16 09:54:55 -08:00
|
|
|
if (procDetails->procInterface()) {
|
2022-06-30 15:27:28 -07:00
|
|
|
// procedure with a pure interface
|
2023-06-22 09:25:03 -07:00
|
|
|
return IsPureProcedureImpl(*procDetails->procInterface(), set);
|
[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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
} else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
|
2023-06-22 09:25:03 -07:00
|
|
|
return IsPureProcedureImpl(details->symbol(), set);
|
[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-29 16:39:13 -07:00
|
|
|
} 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)) {
|
2022-11-10 10:24:38 -08:00
|
|
|
if (&*ref == &symbol) {
|
|
|
|
return false; // error recovery, recursion is caught elsewhere
|
|
|
|
}
|
2023-06-22 09:25:03 -07:00
|
|
|
if (IsFunction(*ref) && !IsPureProcedureImpl(*ref, set)) {
|
[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-29 16:39:13 -07:00
|
|
|
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-12 15: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-29 16:39:13 -07: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));
|
|
|
|
}
|
|
|
|
|
2023-06-22 09:25:03 -07:00
|
|
|
bool IsPureProcedure(const Symbol &original) {
|
|
|
|
semantics::UnorderedSymbolSet set;
|
|
|
|
return IsPureProcedureImpl(original, set);
|
|
|
|
}
|
|
|
|
|
[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-29 16:39:13 -07:00
|
|
|
bool IsPureProcedure(const Scope &scope) {
|
|
|
|
const Symbol *symbol{scope.GetSymbol()};
|
|
|
|
return symbol && IsPureProcedure(*symbol);
|
|
|
|
}
|
|
|
|
|
2023-08-11 14:29:44 -07:00
|
|
|
bool IsExplicitlyImpureProcedure(const Symbol &original) {
|
|
|
|
// An ENTRY is IMPURE if its containing subprogram is so
|
|
|
|
return DEREF(GetMainEntry(&original.GetUltimate()))
|
|
|
|
.attrs()
|
|
|
|
.test(Attr::IMPURE);
|
|
|
|
}
|
|
|
|
|
2022-06-30 15:27:28 -07:00
|
|
|
bool IsElementalProcedure(const Symbol &original) {
|
|
|
|
// An ENTRY is elemental if its containing subprogram is
|
|
|
|
const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
|
2023-08-11 14:29:44 -07:00
|
|
|
if (IsProcedure(symbol)) {
|
|
|
|
auto &foldingContext{symbol.owner().context().foldingContext()};
|
|
|
|
auto restorer{foldingContext.messages().DiscardMessages()};
|
|
|
|
auto proc{evaluate::characteristics::Procedure::Characterize(
|
|
|
|
symbol, foldingContext)};
|
|
|
|
return proc &&
|
|
|
|
proc->attrs.test(evaluate::characteristics::Procedure::Attr::Elemental);
|
|
|
|
} else {
|
2022-06-30 15:27:28 -07: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-29 16:39:13 -07:00
|
|
|
bool IsFunction(const Symbol &symbol) {
|
2022-02-04 18:04:58 -08:00
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
return ultimate.test(Symbol::Flag::Function) ||
|
2022-05-16 18:10:27 -07:00
|
|
|
(!ultimate.test(Symbol::Flag::Subroutine) &&
|
|
|
|
common::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const SubprogramDetails &x) { return x.isFunction(); },
|
|
|
|
[](const ProcEntityDetails &x) {
|
2022-12-16 09:54:55 -08:00
|
|
|
const Symbol *ifc{x.procInterface()};
|
|
|
|
return x.type() || (ifc && IsFunction(*ifc));
|
2022-05-16 18:10:27 -07:00
|
|
|
},
|
|
|
|
[](const ProcBindingDetails &x) {
|
|
|
|
return IsFunction(x.symbol());
|
|
|
|
},
|
|
|
|
[](const auto &) { return false; },
|
2022-03-23 14:05:50 -07:00
|
|
|
},
|
2022-05-16 18:10:27 -07:00
|
|
|
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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
|
2021-01-19 17: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-29 16:39:13 -07:00
|
|
|
bool IsProcedure(const Symbol &symbol) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(common::visitors{
|
2022-11-30 16:11:20 -08:00
|
|
|
[&symbol](const SubprogramDetails &) {
|
|
|
|
const Scope *scope{symbol.scope()};
|
|
|
|
// Main programs & BLOCK DATA are not procedures.
|
|
|
|
return !scope ||
|
|
|
|
scope->kind() == Scope::Kind::Subprogram;
|
|
|
|
},
|
2022-03-23 14:05:50 -07:00
|
|
|
[](const SubprogramNameDetails &) { return true; },
|
|
|
|
[](const ProcEntityDetails &) { return true; },
|
|
|
|
[](const GenericDetails &) { return true; },
|
|
|
|
[](const ProcBindingDetails &) { return true; },
|
|
|
|
[](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-12 15:36:45 -08:00
|
|
|
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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
|
2021-01-19 17:14:41 -08:00
|
|
|
bool IsProcedure(const Scope &scope) {
|
|
|
|
const Symbol *symbol{scope.GetSymbol()};
|
|
|
|
return symbol && IsProcedure(*symbol);
|
|
|
|
}
|
|
|
|
|
2023-08-21 12:21:49 -07:00
|
|
|
bool IsProcedurePointer(const Symbol &original) {
|
|
|
|
const Symbol &symbol{GetAssociationRoot(original)};
|
|
|
|
return IsPointer(symbol) && IsProcedure(symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsProcedurePointer(const Symbol *symbol) {
|
|
|
|
return symbol && IsProcedurePointer(*symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsObjectPointer(const Symbol *original) {
|
|
|
|
if (original) {
|
|
|
|
const Symbol &symbol{GetAssociationRoot(*original)};
|
|
|
|
return IsPointer(symbol) && !IsProcedure(symbol);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsAllocatableOrObjectPointer(const Symbol *original) {
|
|
|
|
if (original) {
|
2023-09-19 12:27:35 -07:00
|
|
|
const Symbol &ultimate{original->GetUltimate()};
|
|
|
|
if (const auto *assoc{ultimate.detailsIf<AssocEntityDetails>()}) {
|
|
|
|
// Only SELECT RANK construct entities can be ALLOCATABLE/POINTER.
|
|
|
|
return (assoc->rank() || assoc->IsAssumedSize() ||
|
|
|
|
assoc->IsAssumedRank()) &&
|
|
|
|
IsAllocatableOrObjectPointer(UnwrapWholeSymbolDataRef(assoc->expr()));
|
|
|
|
} else {
|
|
|
|
return IsAllocatable(ultimate) ||
|
|
|
|
(IsPointer(ultimate) && !IsProcedure(ultimate));
|
|
|
|
}
|
2023-08-21 12:21:49 -07:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-19 17:14:41 -08:00
|
|
|
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-29 16:39:13 -07:00
|
|
|
return details ? details->commonBlock() : nullptr;
|
|
|
|
}
|
|
|
|
|
[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-18 11: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-18 17:17:04 -07: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-12 15:36:45 -08:00
|
|
|
const Symbol &symbol{GetAssociationRoot(original)};
|
|
|
|
const Scope &scope{symbol.owner()};
|
2022-09-28 08:35:08 +02:00
|
|
|
const common::LanguageFeatureControl &features{
|
|
|
|
scope.context().languageFeatures()};
|
[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-12 15:36:45 -08:00
|
|
|
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-19 11:53:20 -07:00
|
|
|
} else if (symbol.attrs().test(Attr::SAVE)) {
|
2025-02-27 14:28:08 -08:00
|
|
|
// explicit or implied SAVE attribute
|
|
|
|
// N.B.: semantics sets implied SAVE for main program
|
|
|
|
// local variables whose derived types have coarray
|
|
|
|
// potential subobject components.
|
|
|
|
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-18 11:48:42 -08:00
|
|
|
} else if (IsDummy(symbol) || IsFunctionResult(symbol) ||
|
2021-11-26 12: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-18 11:48:42 -08:00
|
|
|
return false;
|
|
|
|
} else if (scopeKind == Scope::Kind::Module ||
|
|
|
|
(scopeKind == Scope::Kind::MainProgram &&
|
2024-12-16 12:34:01 -08:00
|
|
|
(symbol.attrs().test(Attr::TARGET) || evaluate::IsCoarray(symbol)) &&
|
|
|
|
Fortran::evaluate::CanCUDASymbolHaveSaveAttr(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-18 11:48:42 -08:00
|
|
|
// 8.5.16p4
|
|
|
|
// In main programs, implied SAVE matters only for pointer
|
|
|
|
// initialization targets and coarrays.
|
|
|
|
return true;
|
2022-09-28 08:35:08 +02:00
|
|
|
} else if (scopeKind == Scope::Kind::MainProgram &&
|
|
|
|
(features.IsEnabled(common::LanguageFeature::SaveMainProgram) ||
|
|
|
|
(features.IsEnabled(
|
|
|
|
common::LanguageFeature::SaveBigMainProgramVariables) &&
|
2024-08-08 13:12:59 -07:00
|
|
|
symbol.size() > 32)) &&
|
2024-09-18 22:14:30 -07:00
|
|
|
Fortran::evaluate::CanCUDASymbolHaveSaveAttr(symbol)) {
|
2022-09-28 08:35:08 +02:00
|
|
|
// With SaveBigMainProgramVariables, keeping all unsaved main program
|
|
|
|
// variables of 32 bytes or less on the stack allows keeping numerical and
|
|
|
|
// logical scalars, small scalar characters or derived, small arrays, and
|
|
|
|
// scalar descriptors on the stack. This leaves more room for lower level
|
|
|
|
// optimizers to do register promotion or get easy aliasing information.
|
|
|
|
return true;
|
|
|
|
} else if (features.IsEnabled(common::LanguageFeature::DefaultSave) &&
|
2022-03-15 09:32:43 +01:00
|
|
|
(scopeKind == Scope::Kind::MainProgram ||
|
|
|
|
(scope.kind() == Scope::Kind::Subprogram &&
|
|
|
|
!(scope.symbol() &&
|
|
|
|
scope.symbol()->attrs().test(Attr::RECURSIVE))))) {
|
|
|
|
// -fno-automatic/-save/-Msave option applies to all objects in executable
|
|
|
|
// main programs and subprograms unless they are explicitly RECURSIVE.
|
[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-18 11:48:42 -08:00
|
|
|
return true;
|
2021-11-26 12: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-12 15:36:45 -08:00
|
|
|
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
|
|
|
|
object && object->init()) {
|
|
|
|
return true;
|
2022-05-28 11:54:57 -07:00
|
|
|
} else if (IsProcedurePointer(symbol) && symbol.has<ProcEntityDetails>() &&
|
[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-12 15:36:45 -08:00
|
|
|
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-18 11: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-12 15: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-18 11: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-12 15: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-18 11: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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsDummy(const Symbol &symbol) {
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
[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-29 16:39:13 -07:00
|
|
|
common::visitors{[](const EntityDetails &x) { return x.isDummy(); },
|
|
|
|
[](const ObjectEntityDetails &x) { return x.isDummy(); },
|
|
|
|
[](const ProcEntityDetails &x) { return x.isDummy(); },
|
2021-07-22 14:53:50 -07: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-29 16:39:13 -07: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-12 15: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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
|
2021-11-26 13:26:50 -08:00
|
|
|
bool IsAssumedShape(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{ResolveAssociations(symbol)};
|
|
|
|
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
2024-01-15 12:08:00 -08:00
|
|
|
return object && object->IsAssumedShape() &&
|
2023-08-21 12:21:49 -07:00
|
|
|
!semantics::IsAllocatableOrObjectPointer(&ultimate);
|
2021-11-26 13:26:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsDeferredShape(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{ResolveAssociations(symbol)};
|
|
|
|
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
|
|
|
return object && object->CanBeDeferredShape() &&
|
2023-08-21 12:21:49 -07:00
|
|
|
semantics::IsAllocatableOrObjectPointer(&ultimate);
|
2021-11-26 13:26:50 -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-12 15:36:45 -08:00
|
|
|
bool IsFunctionResult(const Symbol &original) {
|
|
|
|
const Symbol &symbol{GetAssociationRoot(original)};
|
2022-05-16 18:10:27 -07:00
|
|
|
return common::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const EntityDetails &x) { return x.isFuncResult(); },
|
|
|
|
[](const ObjectEntityDetails &x) { return x.isFuncResult(); },
|
|
|
|
[](const ProcEntityDetails &x) { return x.isFuncResult(); },
|
|
|
|
[](const auto &) { return false; },
|
|
|
|
},
|
|
|
|
symbol.details());
|
2020-07-09 11:08:41 -07:00
|
|
|
}
|
|
|
|
|
[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-07 12: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-12 15: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-07 12: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-12 15: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-07 12:08:58 -08:00
|
|
|
return param && param->attr() == common::TypeParamAttr::Len;
|
|
|
|
}
|
|
|
|
|
2021-09-21 16:06:30 -07:00
|
|
|
bool IsExtensibleType(const DerivedTypeSpec *derived) {
|
2024-01-02 09:04:26 -08:00
|
|
|
return !IsSequenceOrBindCType(derived) && !IsIsoCType(derived);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsSequenceOrBindCType(const DerivedTypeSpec *derived) {
|
|
|
|
return derived &&
|
|
|
|
(derived->typeSymbol().attrs().test(Attr::BIND_C) ||
|
|
|
|
derived->typeSymbol().get<DerivedTypeDetails>().sequence());
|
2021-09-21 16:06:30 -07:00
|
|
|
}
|
|
|
|
|
2024-06-18 12:46:15 -07:00
|
|
|
static bool IsSameModule(const Scope *x, const Scope *y) {
|
|
|
|
if (x == y) {
|
|
|
|
return true;
|
|
|
|
} else if (x && y) {
|
|
|
|
// Allow for a builtin module to be read from distinct paths
|
|
|
|
const Symbol *xSym{x->symbol()};
|
|
|
|
const Symbol *ySym{y->symbol()};
|
|
|
|
if (xSym && ySym && xSym->name() == ySym->name()) {
|
|
|
|
const auto *xMod{xSym->detailsIf<ModuleDetails>()};
|
|
|
|
const auto *yMod{ySym->detailsIf<ModuleDetails>()};
|
|
|
|
if (xMod && yMod) {
|
|
|
|
auto xHash{xMod->moduleFileHash()};
|
|
|
|
auto yHash{yMod->moduleFileHash()};
|
|
|
|
return xHash && yHash && *xHash == *yHash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-09-21 16:06:30 -07:00
|
|
|
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
|
2024-06-18 12:46:15 -07:00
|
|
|
if (derived) {
|
2021-09-21 16:06:30 -07:00
|
|
|
const auto &symbol{derived->typeSymbol()};
|
2024-06-18 12:46:15 -07:00
|
|
|
const Scope &scope{symbol.owner()};
|
|
|
|
return symbol.name() == "__builtin_"s + name &&
|
|
|
|
IsSameModule(&scope, scope.context().GetBuiltinsScope());
|
|
|
|
} else {
|
|
|
|
return false;
|
2021-09-21 16:06:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-29 22:29:34 +08:00
|
|
|
bool IsBuiltinCPtr(const Symbol &symbol) {
|
2023-04-21 10:03:17 -07:00
|
|
|
if (const DeclTypeSpec *declType = symbol.GetType()) {
|
|
|
|
if (const DerivedTypeSpec *derived = declType->AsDerived()) {
|
2022-08-29 22:29:34 +08:00
|
|
|
return IsIsoCType(derived);
|
2023-04-21 10:03:17 -07:00
|
|
|
}
|
|
|
|
}
|
2022-08-29 22:29:34 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-09-21 16:06:30 -07:00
|
|
|
bool IsIsoCType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "c_ptr") ||
|
|
|
|
IsBuiltinDerivedType(derived, "c_funptr");
|
|
|
|
}
|
|
|
|
|
2023-05-16 12:09:48 -07:00
|
|
|
bool IsEventType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "event_type");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsLockType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "lock_type");
|
|
|
|
}
|
|
|
|
|
2024-01-02 10:40:47 -08:00
|
|
|
bool IsNotifyType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "notify_type");
|
|
|
|
}
|
|
|
|
|
2024-06-18 12:46:15 -07:00
|
|
|
bool IsIeeeFlagType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "ieee_flag_type");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsIeeeRoundType(const DerivedTypeSpec *derived) {
|
|
|
|
return IsBuiltinDerivedType(derived, "ieee_round_type");
|
|
|
|
}
|
|
|
|
|
2021-09-21 16:06:30 -07:00
|
|
|
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) {
|
2023-05-16 12:09:48 -07:00
|
|
|
return IsEventType(derivedTypeSpec) || IsLockType(derivedTypeSpec);
|
2021-09-21 16:06:30 -07:00
|
|
|
}
|
|
|
|
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-29 16:39:13 -07:00
|
|
|
int CountLenParameters(const DerivedTypeSpec &type) {
|
2022-09-03 11:17:35 -07:00
|
|
|
return llvm::count_if(
|
|
|
|
type.parameters(), [](const auto &pair) { return pair.second.isLen(); });
|
[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-29 16:39:13 -07:00
|
|
|
}
|
|
|
|
|
2020-06-18 17:17:04 -07:00
|
|
|
int CountNonConstantLenParameters(const DerivedTypeSpec &type) {
|
2022-09-03 11:17:35 -07:00
|
|
|
return llvm::count_if(type.parameters(), [](const auto &pair) {
|
|
|
|
if (!pair.second.isLen()) {
|
|
|
|
return false;
|
|
|
|
} else if (const auto &expr{pair.second.GetExplicit()}) {
|
|
|
|
return !IsConstantExpr(*expr);
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
2020-06-18 17:17:04 -07:00
|
|
|
}
|
|
|
|
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-29 16:39:13 -07:00
|
|
|
const Symbol &GetUsedModule(const UseDetails &details) {
|
|
|
|
return DEREF(details.symbol().owner().symbol());
|
|
|
|
}
|
|
|
|
|
2021-03-12 13:51:33 -08:00
|
|
|
static const Symbol *FindFunctionResult(
|
2021-03-18 10:26:23 -07:00
|
|
|
const Symbol &original, UnorderedSymbolSet &seen) {
|
2021-03-12 13:51:33 -08:00
|
|
|
const Symbol &root{GetAssociationRoot(original)};
|
|
|
|
;
|
|
|
|
if (!seen.insert(root).second) {
|
|
|
|
return nullptr; // don't loop
|
|
|
|
}
|
2022-03-23 14:05:50 -07:00
|
|
|
return common::visit(
|
2021-03-12 13:51:33 -08:00
|
|
|
common::visitors{[](const SubprogramDetails &subp) {
|
|
|
|
return subp.isFunction() ? &subp.result() : nullptr;
|
|
|
|
},
|
|
|
|
[&](const ProcEntityDetails &proc) {
|
2022-12-16 09:54:55 -08:00
|
|
|
const Symbol *iface{proc.procInterface()};
|
2021-03-12 13:51:33 -08:00
|
|
|
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-18 10:26:23 -07:00
|
|
|
UnorderedSymbolSet seen;
|
2021-03-12 13:51:33 -08:00
|
|
|
return FindFunctionResult(symbol, seen);
|
|
|
|
}
|
|
|
|
|
2021-03-16 14:32:45 -07: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-18 10:26:23 -07: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-16 14:32:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
SemanticsContext &Symbol::GetSemanticsContext() const {
|
|
|
|
return DEREF(owner_).context();
|
|
|
|
}
|
|
|
|
|
2022-02-09 16:32:58 -08:00
|
|
|
bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y) {
|
|
|
|
if (x && y) {
|
|
|
|
if (auto xDt{evaluate::DynamicType::From(*x)}) {
|
|
|
|
if (auto yDt{evaluate::DynamicType::From(*y)}) {
|
|
|
|
return xDt->IsTkCompatibleWith(*yDt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-10 11:05:03 -07:00
|
|
|
common::IgnoreTKRSet GetIgnoreTKR(const Symbol &symbol) {
|
|
|
|
common::IgnoreTKRSet result;
|
|
|
|
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
result = object->ignoreTKR();
|
|
|
|
if (const Symbol * ownerSymbol{symbol.owner().symbol()}) {
|
|
|
|
if (const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()}) {
|
|
|
|
if (ownerSubp->defaultIgnoreTKR()) {
|
|
|
|
result |= common::ignoreTKRAll;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-11-30 12:22:04 -08:00
|
|
|
std::optional<int> GetDummyArgumentNumber(const Symbol *symbol) {
|
|
|
|
if (symbol) {
|
|
|
|
if (IsDummy(*symbol)) {
|
|
|
|
if (const Symbol * subpSym{symbol->owner().symbol()}) {
|
|
|
|
if (const auto *subp{subpSym->detailsIf<SubprogramDetails>()}) {
|
|
|
|
int j{0};
|
|
|
|
for (const Symbol *dummy : subp->dummyArgs()) {
|
|
|
|
if (dummy == symbol) {
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2024-11-19 16:20:23 -08:00
|
|
|
// Given a symbol that is a SubprogramNameDetails in a submodule, try to
|
|
|
|
// find its interface definition in its module or ancestor submodule.
|
|
|
|
const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule) {
|
|
|
|
if (symInSubmodule && symInSubmodule->owner().IsSubmodule()) {
|
|
|
|
if (const auto *nameDetails{
|
|
|
|
symInSubmodule->detailsIf<semantics::SubprogramNameDetails>()};
|
|
|
|
nameDetails &&
|
|
|
|
nameDetails->kind() == semantics::SubprogramKind::Module) {
|
|
|
|
const Symbol *next{symInSubmodule->owner().symbol()};
|
|
|
|
while (const Symbol * submodSym{next}) {
|
|
|
|
next = nullptr;
|
|
|
|
if (const auto *modDetails{
|
|
|
|
submodSym->detailsIf<semantics::ModuleDetails>()};
|
|
|
|
modDetails && modDetails->isSubmodule() && modDetails->scope()) {
|
|
|
|
if (const semantics::Scope & parent{modDetails->scope()->parent()};
|
|
|
|
parent.IsSubmodule() || parent.IsModule()) {
|
|
|
|
if (auto iter{parent.find(symInSubmodule->name())};
|
|
|
|
iter != parent.end()) {
|
|
|
|
const Symbol &proc{iter->second->GetUltimate()};
|
|
|
|
if (IsProcedure(proc)) {
|
|
|
|
return &proc;
|
|
|
|
}
|
|
|
|
} else if (parent.IsSubmodule()) {
|
|
|
|
next = parent.symbol();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
[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-29 16:39:13 -07:00
|
|
|
} // namespace Fortran::semantics
|