2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Evaluate/variable.cpp -----------------------------------------===//
|
2018-07-07 06:12:33 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-07-07 06:12:33 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-07-07 06:12:33 +08:00
|
|
|
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Evaluate/variable.h"
|
|
|
|
#include "flang/Common/idioms.h"
|
2020-07-08 06:30:47 +08:00
|
|
|
#include "flang/Evaluate/check-expression.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Evaluate/fold.h"
|
|
|
|
#include "flang/Evaluate/tools.h"
|
|
|
|
#include "flang/Parser/char-block.h"
|
|
|
|
#include "flang/Parser/characters.h"
|
|
|
|
#include "flang/Parser/message.h"
|
2021-01-31 02:14:07 +08:00
|
|
|
#include "flang/Semantics/scope.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Semantics/symbol.h"
|
2018-10-27 06:10:24 +08:00
|
|
|
#include <type_traits>
|
2018-07-07 06:12:33 +08:00
|
|
|
|
2018-08-09 02:29:05 +08:00
|
|
|
using namespace Fortran::parser::literals;
|
|
|
|
|
2018-07-07 06:12:33 +08:00
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
2018-07-13 07:07:43 +08:00
|
|
|
// Constructors, accessors, mutators
|
|
|
|
|
2019-01-23 08:30:32 +08:00
|
|
|
Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}
|
|
|
|
|
2018-08-15 04:39:59 +08:00
|
|
|
Triplet::Triplet(std::optional<Expr<SubscriptInteger>> &&l,
|
|
|
|
std::optional<Expr<SubscriptInteger>> &&u,
|
2019-01-23 08:30:32 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> &&s)
|
2020-03-29 12:00:16 +08:00
|
|
|
: stride_{s ? std::move(*s) : Expr<SubscriptInteger>{1}} {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (l) {
|
2019-01-23 08:30:32 +08:00
|
|
|
lower_.emplace(std::move(*l));
|
2018-07-07 06:12:33 +08:00
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (u) {
|
2019-01-23 08:30:32 +08:00
|
|
|
upper_.emplace(std::move(*u));
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-15 04:39:59 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> Triplet::lower() const {
|
2018-07-13 07:07:43 +08:00
|
|
|
if (lower_) {
|
2019-03-06 04:28:08 +08:00
|
|
|
return {lower_.value().value()};
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
2018-07-26 06:02:21 +08:00
|
|
|
return std::nullopt;
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
|
2019-05-09 01:22:56 +08:00
|
|
|
Triplet &Triplet::set_lower(Expr<SubscriptInteger> &&expr) {
|
|
|
|
lower_.emplace(std::move(expr));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2018-08-15 04:39:59 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> Triplet::upper() const {
|
2018-07-13 07:07:43 +08:00
|
|
|
if (upper_) {
|
2019-03-06 04:28:08 +08:00
|
|
|
return {upper_.value().value()};
|
2018-07-07 06:12:33 +08:00
|
|
|
}
|
2018-07-26 06:02:21 +08:00
|
|
|
return std::nullopt;
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
|
2019-05-09 01:22:56 +08:00
|
|
|
Triplet &Triplet::set_upper(Expr<SubscriptInteger> &&expr) {
|
|
|
|
upper_.emplace(std::move(expr));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2019-04-04 07:04:13 +08:00
|
|
|
Expr<SubscriptInteger> Triplet::stride() const { return stride_.value(); }
|
2019-01-23 08:30:32 +08:00
|
|
|
|
2019-05-09 01:22:56 +08:00
|
|
|
Triplet &Triplet::set_stride(Expr<SubscriptInteger> &&expr) {
|
|
|
|
stride_.value() = std::move(expr);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2019-01-23 08:30:32 +08:00
|
|
|
bool Triplet::IsStrideOne() const {
|
2019-03-06 04:28:08 +08:00
|
|
|
if (auto stride{ToInt64(stride_.value())}) {
|
2019-01-23 08:30:32 +08:00
|
|
|
return stride == 1;
|
|
|
|
} else {
|
|
|
|
return false;
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 07:53:29 +08:00
|
|
|
CoarrayRef::CoarrayRef(SymbolVector &&base, std::vector<Subscript> &&ss,
|
|
|
|
std::vector<Expr<SubscriptInteger>> &&css)
|
2020-03-29 12:00:16 +08:00
|
|
|
: base_{std::move(base)}, subscript_(std::move(ss)),
|
|
|
|
cosubscript_(std::move(css)) {
|
2019-04-04 03:57:24 +08:00
|
|
|
CHECK(!base_.empty());
|
|
|
|
CHECK(!cosubscript_.empty());
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
|
2019-01-05 06:05:53 +08:00
|
|
|
std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (stat_) {
|
|
|
|
return stat_.value().value();
|
2019-01-05 06:05:53 +08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (team_) {
|
|
|
|
return team_.value().value();
|
2019-01-05 06:05:53 +08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:24:57 +08:00
|
|
|
CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
|
|
|
|
CHECK(IsVariable(v));
|
2019-01-23 08:30:32 +08:00
|
|
|
stat_.emplace(std::move(v));
|
2018-07-13 07:07:43 +08:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:24:57 +08:00
|
|
|
CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&v, bool isTeamNumber) {
|
|
|
|
CHECK(IsVariable(v));
|
2019-01-23 08:30:32 +08:00
|
|
|
team_.emplace(std::move(v));
|
2018-07-13 07:07:43 +08:00
|
|
|
teamIsTeamNumber_ = isTeamNumber;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &CoarrayRef::GetFirstSymbol() const { return base_.front(); }
|
2019-04-02 06:22:45 +08:00
|
|
|
|
2019-10-23 07:53:29 +08:00
|
|
|
const Symbol &CoarrayRef::GetLastSymbol() const { return base_.back(); }
|
2019-04-02 06:22:45 +08:00
|
|
|
|
2018-11-14 08:02:52 +08:00
|
|
|
void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &lower,
|
|
|
|
std::optional<Expr<SubscriptInteger>> &upper) {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (lower) {
|
2019-05-09 01:22:56 +08:00
|
|
|
set_lower(std::move(lower.value()));
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (upper) {
|
2019-05-09 01:22:56 +08:00
|
|
|
set_upper(std::move(upper.value()));
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-14 08:02:52 +08:00
|
|
|
Expr<SubscriptInteger> Substring::lower() const {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (lower_) {
|
2019-03-06 04:28:08 +08:00
|
|
|
return lower_.value().value();
|
2018-10-27 06:10:24 +08:00
|
|
|
} else {
|
|
|
|
return AsExpr(Constant<SubscriptInteger>{1});
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-09 01:22:56 +08:00
|
|
|
Substring &Substring::set_lower(Expr<SubscriptInteger> &&expr) {
|
|
|
|
lower_.emplace(std::move(expr));
|
|
|
|
return *this;
|
2019-05-08 07:19:45 +08:00
|
|
|
}
|
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> Substring::upper() const {
|
2019-11-10 01:29:31 +08:00
|
|
|
if (upper_) {
|
2019-03-06 04:28:08 +08:00
|
|
|
return upper_.value().value();
|
2018-10-27 06:10:24 +08:00
|
|
|
} else {
|
2018-11-03 05:13:12 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
|
|
|
[](const DataRef &dataRef) { return dataRef.LEN(); },
|
2019-07-04 01:25:43 +08:00
|
|
|
[](const StaticDataObject::Pointer &object)
|
|
|
|
-> std::optional<Expr<SubscriptInteger>> {
|
2018-11-03 05:13:12 +08:00
|
|
|
return AsExpr(Constant<SubscriptInteger>{object->data().size()});
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-11-03 05:13:12 +08:00
|
|
|
parent_);
|
2018-07-13 07:07:43 +08:00
|
|
|
}
|
2018-07-07 06:12:33 +08:00
|
|
|
}
|
|
|
|
|
2019-05-09 01:22:56 +08:00
|
|
|
Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {
|
|
|
|
upper_.emplace(std::move(expr));
|
|
|
|
return *this;
|
2019-05-08 07:19:45 +08:00
|
|
|
}
|
|
|
|
|
2018-11-06 05:48:00 +08:00
|
|
|
std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
|
2022-01-07 05:31:37 +08:00
|
|
|
if (!upper_) {
|
|
|
|
upper_ = upper();
|
|
|
|
if (!upper_) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));
|
|
|
|
std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())};
|
|
|
|
if (!ubi) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!lower_) {
|
2018-11-14 08:02:52 +08:00
|
|
|
lower_ = AsExpr(Constant<SubscriptInteger>{1});
|
2018-08-09 02:29:05 +08:00
|
|
|
}
|
2019-03-06 04:28:08 +08:00
|
|
|
lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));
|
2019-06-07 04:48:26 +08:00
|
|
|
std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};
|
2022-01-07 05:31:37 +08:00
|
|
|
if (!lbi) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
if (*lbi > *ubi) { // empty result; canonicalize
|
2018-11-02 02:18:12 +08:00
|
|
|
*lbi = 1;
|
2022-01-07 05:31:37 +08:00
|
|
|
*ubi = 0;
|
|
|
|
lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
|
|
|
|
upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
|
2018-08-09 02:29:05 +08:00
|
|
|
}
|
2022-01-07 05:31:37 +08:00
|
|
|
std::optional<ConstantSubscript> length;
|
|
|
|
std::optional<Expr<SomeCharacter>> strings; // a Constant<Character>
|
|
|
|
if (const auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)}) {
|
|
|
|
length = (*literal)->data().size();
|
|
|
|
if (auto str{(*literal)->AsString()}) {
|
|
|
|
strings =
|
|
|
|
Expr<SomeCharacter>(Expr<Ascii>(Constant<Ascii>{std::move(*str)}));
|
2019-07-09 07:02:50 +08:00
|
|
|
}
|
2022-01-07 05:31:37 +08:00
|
|
|
} else if (const auto *dataRef{std::get_if<DataRef>(&parent_)}) {
|
|
|
|
if (auto expr{AsGenericExpr(DataRef{*dataRef})}) {
|
|
|
|
auto folded{evaluate::Fold(context, std::move(*expr))};
|
|
|
|
if (IsActuallyConstant(folded)) {
|
|
|
|
if (const auto *value{UnwrapExpr<Expr<SomeCharacter>>(folded)}) {
|
|
|
|
strings = *value;
|
2019-01-23 08:30:32 +08:00
|
|
|
}
|
|
|
|
}
|
2018-11-03 07:42:45 +08:00
|
|
|
}
|
2022-01-07 05:31:37 +08:00
|
|
|
}
|
|
|
|
std::optional<Expr<SomeCharacter>> result;
|
|
|
|
if (strings) {
|
|
|
|
result = std::visit(
|
|
|
|
[&](const auto &expr) -> std::optional<Expr<SomeCharacter>> {
|
|
|
|
using Type = typename std::decay_t<decltype(expr)>::Result;
|
|
|
|
if (const auto *cc{std::get_if<Constant<Type>>(&expr.u)}) {
|
|
|
|
if (auto substr{cc->Substring(*lbi, *ubi)}) {
|
|
|
|
return Expr<SomeCharacter>{Expr<Type>{*substr}};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
strings->u);
|
|
|
|
}
|
|
|
|
if (!result) { // error cases
|
|
|
|
if (*lbi < 1) {
|
|
|
|
context.messages().Say(
|
|
|
|
"Lower bound (%jd) on substring is less than one"_en_US,
|
|
|
|
static_cast<std::intmax_t>(*lbi));
|
|
|
|
*lbi = 1;
|
2019-05-16 04:23:31 +08:00
|
|
|
lower_ = AsExpr(Constant<SubscriptInteger>{1});
|
2022-01-07 05:31:37 +08:00
|
|
|
}
|
|
|
|
if (length && *ubi > *length) {
|
|
|
|
context.messages().Say(
|
|
|
|
"Upper bound (%jd) on substring is greater than character length (%jd)"_en_US,
|
|
|
|
static_cast<std::intmax_t>(*ubi),
|
|
|
|
static_cast<std::intmax_t>(*length));
|
|
|
|
*ubi = *length;
|
|
|
|
upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
|
2018-08-09 02:29:05 +08:00
|
|
|
}
|
|
|
|
}
|
2022-01-07 05:31:37 +08:00
|
|
|
return result;
|
2018-08-09 02:29:05 +08:00
|
|
|
}
|
|
|
|
|
2019-04-02 06:22:45 +08:00
|
|
|
DescriptorInquiry::DescriptorInquiry(
|
2019-06-26 04:07:32 +08:00
|
|
|
const NamedEntity &base, Field field, int dim)
|
2020-03-29 12:00:16 +08:00
|
|
|
: base_{base}, field_{field}, dimension_{dim} {
|
2019-06-26 04:07:32 +08:00
|
|
|
const Symbol &last{base_.GetLastSymbol()};
|
|
|
|
CHECK(IsDescriptor(last));
|
2021-09-14 04:45:30 +08:00
|
|
|
CHECK(((field == Field::Len || field == Field::Rank) && dim == 0) ||
|
2019-12-12 07:06:24 +08:00
|
|
|
(field != Field::Len && dim >= 0 && dim < last.Rank()));
|
2019-04-02 06:22:45 +08:00
|
|
|
}
|
2019-06-26 04:07:32 +08:00
|
|
|
|
|
|
|
DescriptorInquiry::DescriptorInquiry(NamedEntity &&base, Field field, int dim)
|
2020-03-29 12:00:16 +08:00
|
|
|
: base_{std::move(base)}, field_{field}, dimension_{dim} {
|
2019-06-26 04:07:32 +08:00
|
|
|
const Symbol &last{base_.GetLastSymbol()};
|
|
|
|
CHECK(IsDescriptor(last));
|
2019-12-12 07:06:24 +08:00
|
|
|
CHECK((field == Field::Len && dim == 0) ||
|
|
|
|
(field != Field::Len && dim >= 0 && dim < last.Rank()));
|
2019-04-02 06:22:45 +08:00
|
|
|
}
|
|
|
|
|
2018-07-12 05:50:08 +08:00
|
|
|
// LEN()
|
2021-01-31 02:14:07 +08:00
|
|
|
static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &symbol) {
|
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
if (const auto *assoc{ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
|
|
|
|
if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc->expr())}) {
|
|
|
|
return chExpr->LEN();
|
|
|
|
}
|
2021-11-16 05:04:02 +08:00
|
|
|
}
|
|
|
|
if (auto dyType{DynamicType::From(ultimate)}) {
|
2021-06-03 08:13:55 +08:00
|
|
|
if (auto len{dyType->GetCharLength()}) {
|
2021-11-16 05:04:02 +08:00
|
|
|
if (ultimate.owner().IsDerivedType() || IsScopeInvariantExpr(*len)) {
|
|
|
|
return AsExpr(Extremum<SubscriptInteger>{
|
|
|
|
Ordering::Greater, Expr<SubscriptInteger>{0}, std::move(*len)});
|
|
|
|
}
|
2019-07-04 01:25:43 +08:00
|
|
|
}
|
|
|
|
}
|
2021-11-16 05:04:02 +08:00
|
|
|
if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
|
|
|
|
return Expr<SubscriptInteger>{
|
|
|
|
DescriptorInquiry{NamedEntity{symbol}, DescriptorInquiry::Field::Len}};
|
|
|
|
}
|
2019-07-04 01:25:43 +08:00
|
|
|
return std::nullopt;
|
2018-07-12 05:50:08 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {
|
2018-11-03 05:13:12 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
2019-10-23 07:53:29 +08:00
|
|
|
[](const Symbol &symbol) { return SymbolLEN(symbol); },
|
2019-07-04 01:25:43 +08:00
|
|
|
[](const StaticDataObject::Pointer &object)
|
|
|
|
-> std::optional<Expr<SubscriptInteger>> {
|
2018-11-03 05:13:12 +08:00
|
|
|
return AsExpr(Constant<SubscriptInteger>{object->data().size()});
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-11-03 05:13:12 +08:00
|
|
|
u);
|
|
|
|
}
|
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> Component::LEN() const {
|
2018-11-02 02:18:12 +08:00
|
|
|
return SymbolLEN(GetLastSymbol());
|
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {
|
2019-06-26 04:07:32 +08:00
|
|
|
return SymbolLEN(GetLastSymbol());
|
2018-07-12 05:50:08 +08:00
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {
|
|
|
|
return base_.LEN();
|
|
|
|
}
|
2019-06-26 04:07:32 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {
|
2019-04-02 06:22:45 +08:00
|
|
|
return SymbolLEN(GetLastSymbol());
|
2018-07-13 07:58:21 +08:00
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return SymbolLEN(symbol); },
|
|
|
|
[](const auto &x) { return x.LEN(); },
|
|
|
|
},
|
2018-09-15 06:48:40 +08:00
|
|
|
u);
|
2018-07-12 05:50:08 +08:00
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
|
|
|
|
if (auto top{upper()}) {
|
2019-10-31 23:03:16 +08:00
|
|
|
return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
|
|
|
|
AsExpr(Constant<SubscriptInteger>{0}),
|
|
|
|
*std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
|
2019-07-04 01:25:43 +08:00
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2018-07-12 05:50:08 +08:00
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> Designator<T>::LEN() const {
|
|
|
|
if constexpr (T::category == TypeCategory::Character) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return SymbolLEN(symbol); },
|
|
|
|
[](const auto &x) { return x.LEN(); },
|
|
|
|
},
|
2018-10-31 04:30:56 +08:00
|
|
|
u);
|
|
|
|
} else {
|
2019-07-04 01:25:43 +08:00
|
|
|
common::die("Designator<non-char>::LEN() called");
|
|
|
|
return std::nullopt;
|
2018-10-31 04:30:56 +08:00
|
|
|
}
|
2018-09-15 06:48:40 +08:00
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
|
2019-07-04 01:25:43 +08:00
|
|
|
std::optional<Expr<SubscriptInteger>> ProcedureDesignator::LEN() const {
|
|
|
|
using T = std::optional<Expr<SubscriptInteger>>;
|
2018-07-12 05:50:08 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
2019-10-23 07:53:29 +08:00
|
|
|
[](SymbolRef symbol) -> T { return SymbolLEN(symbol); },
|
2019-07-04 01:25:43 +08:00
|
|
|
[](const common::CopyableIndirection<Component> &c) -> T {
|
2019-06-28 06:58:23 +08:00
|
|
|
return c.value().LEN();
|
|
|
|
},
|
2019-07-04 01:25:43 +08:00
|
|
|
[](const SpecificIntrinsic &i) -> T {
|
2021-06-03 08:13:55 +08:00
|
|
|
// Some cases whose results' lengths can be determined
|
2019-07-04 01:25:43 +08:00
|
|
|
// from the lengths of their arguments are handled in
|
2021-06-03 08:13:55 +08:00
|
|
|
// ProcedureRef::LEN() before coming here.
|
|
|
|
if (const auto &result{i.characteristics.value().functionResult}) {
|
|
|
|
if (const auto *type{result->GetTypeAndShape()}) {
|
|
|
|
if (auto length{type->type().GetCharLength()}) {
|
|
|
|
return std::move(*length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-04 01:25:43 +08:00
|
|
|
return std::nullopt;
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-09-15 06:48:40 +08:00
|
|
|
u);
|
2018-07-12 05:50:08 +08:00
|
|
|
}
|
|
|
|
|
2018-09-19 00:34:59 +08:00
|
|
|
// Rank()
|
2018-11-03 05:13:12 +08:00
|
|
|
int BaseObject::Rank() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return symbol->Rank(); },
|
|
|
|
[](const StaticDataObject::Pointer &) { return 0; },
|
|
|
|
},
|
2018-11-03 05:13:12 +08:00
|
|
|
u);
|
|
|
|
}
|
|
|
|
|
2018-09-20 05:27:13 +08:00
|
|
|
int Component::Rank() const {
|
2019-02-01 01:58:40 +08:00
|
|
|
if (int rank{symbol_->Rank()}; rank > 0) {
|
|
|
|
return rank;
|
|
|
|
}
|
2019-05-30 04:22:35 +08:00
|
|
|
return base().Rank();
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
int NamedEntity::Rank() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](const SymbolRef s) { return s->Rank(); },
|
|
|
|
[](const Component &c) { return c.Rank(); },
|
|
|
|
},
|
2019-06-26 04:07:32 +08:00
|
|
|
u_);
|
|
|
|
}
|
|
|
|
|
2018-09-19 00:34:59 +08:00
|
|
|
int Subscript::Rank() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](const IndirectSubscriptIntegerExpr &x) {
|
|
|
|
return x.value().Rank();
|
|
|
|
},
|
|
|
|
[](const Triplet &) { return 1; },
|
|
|
|
},
|
2018-09-19 00:34:59 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2018-09-19 00:34:59 +08:00
|
|
|
int ArrayRef::Rank() const {
|
|
|
|
int rank{0};
|
2019-02-01 08:04:17 +08:00
|
|
|
for (const auto &expr : subscript_) {
|
2019-02-01 01:58:40 +08:00
|
|
|
rank += expr.Rank();
|
2018-09-19 00:34:59 +08:00
|
|
|
}
|
2019-02-01 01:58:40 +08:00
|
|
|
if (rank > 0) {
|
2018-09-21 03:34:29 +08:00
|
|
|
return rank;
|
2019-06-26 04:07:32 +08:00
|
|
|
} else if (const Component * component{base_.UnwrapComponent()}) {
|
|
|
|
return component->base().Rank();
|
|
|
|
} else {
|
|
|
|
return 0;
|
2018-09-21 03:34:29 +08:00
|
|
|
}
|
2018-09-19 00:34:59 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-04-04 03:57:24 +08:00
|
|
|
int CoarrayRef::Rank() const {
|
2019-04-04 07:04:13 +08:00
|
|
|
if (!subscript_.empty()) {
|
|
|
|
int rank{0};
|
|
|
|
for (const auto &expr : subscript_) {
|
|
|
|
rank += expr.Rank();
|
|
|
|
}
|
2019-04-04 03:57:24 +08:00
|
|
|
return rank;
|
|
|
|
} else {
|
|
|
|
return base_.back()->Rank();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-19 00:34:59 +08:00
|
|
|
int DataRef::Rank() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return symbol->Rank(); },
|
|
|
|
[](const auto &x) { return x.Rank(); },
|
|
|
|
},
|
2018-09-19 00:34:59 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
|
|
|
int Substring::Rank() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](const DataRef &dataRef) { return dataRef.Rank(); },
|
|
|
|
[](const StaticDataObject::Pointer &) { return 0; },
|
|
|
|
},
|
2018-11-03 05:13:12 +08:00
|
|
|
parent_);
|
|
|
|
}
|
|
|
|
|
2018-09-19 00:34:59 +08:00
|
|
|
int ComplexPart::Rank() const { return complex_.Rank(); }
|
2019-10-23 07:53:29 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> int Designator<T>::Rank() const {
|
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return symbol->Rank(); },
|
|
|
|
[](const auto &x) { return x.Rank(); },
|
|
|
|
},
|
2018-10-31 04:30:56 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-09-19 00:34:59 +08:00
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
// GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.
|
2018-11-02 02:18:12 +08:00
|
|
|
const Symbol &Component::GetFirstSymbol() const {
|
2019-03-06 04:28:08 +08:00
|
|
|
return base_.value().GetFirstSymbol();
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
const Symbol &NamedEntity::GetFirstSymbol() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef s) -> const Symbol & { return s; },
|
|
|
|
[](const Component &c) -> const Symbol & {
|
|
|
|
return c.GetFirstSymbol();
|
|
|
|
},
|
|
|
|
},
|
2019-06-26 04:07:32 +08:00
|
|
|
u_);
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
const Symbol &NamedEntity::GetLastSymbol() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef s) -> const Symbol & { return s; },
|
|
|
|
[](const Component &c) -> const Symbol & {
|
|
|
|
return c.GetLastSymbol();
|
|
|
|
},
|
|
|
|
},
|
2019-06-26 04:07:32 +08:00
|
|
|
u_);
|
|
|
|
}
|
|
|
|
|
|
|
|
const Component *NamedEntity::UnwrapComponent() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef) -> const Component * { return nullptr; },
|
|
|
|
[](const Component &c) { return &c; },
|
|
|
|
},
|
2019-06-26 04:07:32 +08:00
|
|
|
u_);
|
|
|
|
}
|
|
|
|
|
|
|
|
Component *NamedEntity::UnwrapComponent() {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](SymbolRef &) -> Component * { return nullptr; },
|
|
|
|
[](Component &c) { return &c; },
|
|
|
|
},
|
2019-06-26 04:07:32 +08:00
|
|
|
u_);
|
|
|
|
}
|
|
|
|
|
|
|
|
const Symbol &ArrayRef::GetFirstSymbol() const {
|
|
|
|
return base_.GetFirstSymbol();
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }
|
|
|
|
|
2018-11-02 02:18:12 +08:00
|
|
|
const Symbol &DataRef::GetFirstSymbol() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return *std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return &*symbol; },
|
|
|
|
[](const auto &x) { return &x.GetFirstSymbol(); },
|
|
|
|
},
|
2018-10-31 04:30:56 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2018-11-02 02:18:12 +08:00
|
|
|
const Symbol &DataRef::GetLastSymbol() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return *std::visit(common::visitors{
|
|
|
|
[](SymbolRef symbol) { return &*symbol; },
|
|
|
|
[](const auto &x) { return &x.GetLastSymbol(); },
|
|
|
|
},
|
2018-10-31 04:30:56 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
|
|
|
BaseObject Substring::GetBaseObject() const {
|
2020-03-29 12:00:16 +08:00
|
|
|
return std::visit(common::visitors{
|
|
|
|
[](const DataRef &dataRef) {
|
|
|
|
return BaseObject{dataRef.GetFirstSymbol()};
|
|
|
|
},
|
|
|
|
[](StaticDataObject::Pointer pointer) {
|
|
|
|
return BaseObject{std::move(pointer)};
|
|
|
|
},
|
|
|
|
},
|
2018-11-03 05:13:12 +08:00
|
|
|
parent_);
|
2018-10-31 04:30:56 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
|
|
|
const Symbol *Substring::GetLastSymbol() const {
|
2018-11-30 01:27:34 +08:00
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const DataRef &dataRef) { return &dataRef.GetLastSymbol(); },
|
|
|
|
[](const auto &) -> const Symbol * { return nullptr; },
|
|
|
|
},
|
2018-11-03 05:13:12 +08:00
|
|
|
parent_);
|
2018-09-20 05:27:13 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> BaseObject Designator<T>::GetBaseObject() const {
|
2018-11-03 05:13:12 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
2019-10-23 07:53:29 +08:00
|
|
|
[](SymbolRef symbol) { return BaseObject{symbol}; },
|
|
|
|
[](const Substring &sstring) { return sstring.GetBaseObject(); },
|
2018-11-03 05:13:12 +08:00
|
|
|
[](const auto &x) {
|
2019-10-23 07:53:29 +08:00
|
|
|
#if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
|
2018-11-03 05:13:12 +08:00
|
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
|
|
|
|
Substring>) {
|
|
|
|
return x.GetBaseObject();
|
2019-10-23 07:53:29 +08:00
|
|
|
} else
|
|
|
|
#endif
|
2018-11-03 05:13:12 +08:00
|
|
|
return BaseObject{x.GetFirstSymbol()};
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-10-31 04:30:56 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
|
2018-11-03 05:13:12 +08:00
|
|
|
return std::visit(
|
2018-11-30 01:27:34 +08:00
|
|
|
common::visitors{
|
2019-10-23 07:53:29 +08:00
|
|
|
[](SymbolRef symbol) { return &*symbol; },
|
|
|
|
[](const Substring &sstring) { return sstring.GetLastSymbol(); },
|
2018-11-03 05:13:12 +08:00
|
|
|
[](const auto &x) {
|
2019-10-23 07:53:29 +08:00
|
|
|
#if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
|
2018-11-03 05:13:12 +08:00
|
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
|
|
|
|
Substring>) {
|
|
|
|
return x.GetLastSymbol();
|
2019-10-23 07:53:29 +08:00
|
|
|
} else
|
|
|
|
#endif
|
2018-11-03 05:13:12 +08:00
|
|
|
return &x.GetLastSymbol();
|
2018-11-30 01:27:34 +08:00
|
|
|
},
|
|
|
|
},
|
2018-10-31 04:30:56 +08:00
|
|
|
u);
|
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
|
|
|
std::optional<DynamicType> Designator<T>::GetType() const {
|
2019-01-05 06:05:53 +08:00
|
|
|
if constexpr (IsLengthlessIntrinsicType<Result>) {
|
2020-08-13 23:07:58 +08:00
|
|
|
return Result::GetType();
|
|
|
|
} else if (const Symbol * symbol{GetLastSymbol()}) {
|
|
|
|
return DynamicType::From(*symbol);
|
|
|
|
} else if constexpr (Result::category == TypeCategory::Character) {
|
|
|
|
if (const Substring * substring{std::get_if<Substring>(&u)}) {
|
|
|
|
const auto *parent{substring->GetParentIf<StaticDataObject::Pointer>()};
|
|
|
|
CHECK(parent);
|
|
|
|
return DynamicType{TypeCategory::Character, (*parent)->itemBytes()};
|
|
|
|
}
|
2018-10-31 04:30:56 +08:00
|
|
|
}
|
2020-08-13 23:07:58 +08:00
|
|
|
return std::nullopt;
|
2018-10-31 04:30:56 +08:00
|
|
|
}
|
2018-11-03 05:13:12 +08:00
|
|
|
|
2019-10-23 07:53:29 +08:00
|
|
|
static NamedEntity AsNamedEntity(const SymbolVector &x) {
|
|
|
|
CHECK(!x.empty());
|
|
|
|
NamedEntity result{x.front()};
|
2019-04-04 07:04:13 +08:00
|
|
|
int j{0};
|
2019-10-23 07:53:29 +08:00
|
|
|
for (const Symbol &symbol : x) {
|
2019-06-26 04:07:32 +08:00
|
|
|
if (j++ != 0) {
|
|
|
|
DataRef base{result.IsSymbol() ? DataRef{result.GetLastSymbol()}
|
|
|
|
: DataRef{result.GetComponent()}};
|
2019-10-23 07:53:29 +08:00
|
|
|
result = NamedEntity{Component{std::move(base), symbol}};
|
2019-04-04 07:04:13 +08:00
|
|
|
}
|
|
|
|
}
|
2019-06-26 04:07:32 +08:00
|
|
|
return result;
|
2019-04-04 07:04:13 +08:00
|
|
|
}
|
|
|
|
|
2019-06-26 04:07:32 +08:00
|
|
|
NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); }
|
|
|
|
|
2019-01-05 06:05:53 +08:00
|
|
|
// Equality testing
|
|
|
|
|
2019-11-20 11:10:02 +08:00
|
|
|
// For the purposes of comparing type parameter expressions while
|
|
|
|
// testing the compatibility of procedure characteristics, two
|
|
|
|
// object dummy arguments with the same name are considered equal.
|
2020-03-11 01:28:36 +08:00
|
|
|
static bool AreSameSymbol(const Symbol &x, const Symbol &y) {
|
2019-11-20 11:10:02 +08:00
|
|
|
if (&x == &y) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (x.name() == y.name()) {
|
2019-11-22 05:35:20 +08:00
|
|
|
if (const auto *xObject{x.detailsIf<semantics::ObjectEntityDetails>()}) {
|
2019-11-20 11:10:02 +08:00
|
|
|
if (const auto *yObject{y.detailsIf<semantics::ObjectEntityDetails>()}) {
|
|
|
|
return xObject->isDummy() && yObject->isDummy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-11 01:28:36 +08:00
|
|
|
// Implements operator==() for a union type, using special case handling
|
|
|
|
// for Symbol references.
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename A> static bool TestVariableEquality(const A &x, const A &y) {
|
2020-03-11 01:28:36 +08:00
|
|
|
const SymbolRef *xSymbol{std::get_if<SymbolRef>(&x.u)};
|
|
|
|
if (const SymbolRef * ySymbol{std::get_if<SymbolRef>(&y.u)}) {
|
|
|
|
return xSymbol && AreSameSymbol(*xSymbol, *ySymbol);
|
|
|
|
} else {
|
|
|
|
return x.u == y.u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-05 06:05:53 +08:00
|
|
|
bool BaseObject::operator==(const BaseObject &that) const {
|
2019-11-20 11:10:02 +08:00
|
|
|
return TestVariableEquality(*this, that);
|
2019-01-05 06:05:53 +08:00
|
|
|
}
|
|
|
|
bool Component::operator==(const Component &that) const {
|
2019-10-23 07:53:29 +08:00
|
|
|
return base_ == that.base_ && &*symbol_ == &*that.symbol_;
|
2019-01-05 06:05:53 +08:00
|
|
|
}
|
2019-06-26 04:07:32 +08:00
|
|
|
bool NamedEntity::operator==(const NamedEntity &that) const {
|
2019-11-16 06:26:10 +08:00
|
|
|
if (IsSymbol()) {
|
2019-11-20 11:10:02 +08:00
|
|
|
return that.IsSymbol() &&
|
|
|
|
AreSameSymbol(GetFirstSymbol(), that.GetFirstSymbol());
|
2019-10-23 07:53:29 +08:00
|
|
|
} else {
|
2019-11-16 06:26:10 +08:00
|
|
|
return !that.IsSymbol() && GetComponent() == that.GetComponent();
|
2019-10-23 07:53:29 +08:00
|
|
|
}
|
2019-06-26 04:07:32 +08:00
|
|
|
}
|
2020-08-26 00:40:20 +08:00
|
|
|
bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {
|
2019-10-23 07:53:29 +08:00
|
|
|
return &*parameter_ == &*that.parameter_ && base_ == that.base_;
|
2019-01-05 06:05:53 +08:00
|
|
|
}
|
|
|
|
bool Triplet::operator==(const Triplet &that) const {
|
|
|
|
return lower_ == that.lower_ && upper_ == that.upper_ &&
|
2019-03-06 04:28:08 +08:00
|
|
|
stride_ == that.stride_;
|
2019-01-05 06:05:53 +08:00
|
|
|
}
|
2020-03-11 01:28:36 +08:00
|
|
|
bool Subscript::operator==(const Subscript &that) const { return u == that.u; }
|
2019-01-05 06:05:53 +08:00
|
|
|
bool ArrayRef::operator==(const ArrayRef &that) const {
|
2019-02-01 08:04:17 +08:00
|
|
|
return base_ == that.base_ && subscript_ == that.subscript_;
|
2019-01-05 06:05:53 +08:00
|
|
|
}
|
|
|
|
bool CoarrayRef::operator==(const CoarrayRef &that) const {
|
2019-04-04 03:57:24 +08:00
|
|
|
return base_ == that.base_ && subscript_ == that.subscript_ &&
|
2019-01-05 06:05:53 +08:00
|
|
|
cosubscript_ == that.cosubscript_ && stat_ == that.stat_ &&
|
|
|
|
team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_;
|
|
|
|
}
|
2019-11-20 11:10:02 +08:00
|
|
|
bool DataRef::operator==(const DataRef &that) const {
|
|
|
|
return TestVariableEquality(*this, that);
|
|
|
|
}
|
2019-01-05 06:05:53 +08:00
|
|
|
bool Substring::operator==(const Substring &that) const {
|
|
|
|
return parent_ == that.parent_ && lower_ == that.lower_ &&
|
|
|
|
upper_ == that.upper_;
|
|
|
|
}
|
|
|
|
bool ComplexPart::operator==(const ComplexPart &that) const {
|
|
|
|
return part_ == that.part_ && complex_ == that.complex_;
|
|
|
|
}
|
|
|
|
bool ProcedureRef::operator==(const ProcedureRef &that) const {
|
|
|
|
return proc_ == that.proc_ && arguments_ == that.arguments_;
|
2018-10-12 05:51:14 +08:00
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
2020-03-11 01:28:36 +08:00
|
|
|
bool Designator<T>::operator==(const Designator<T> &that) const {
|
|
|
|
return TestVariableEquality(*this, that);
|
|
|
|
}
|
2019-04-02 06:22:45 +08:00
|
|
|
bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
|
|
|
|
return field_ == that.field_ && base_ == that.base_ &&
|
|
|
|
dimension_ == that.dimension_;
|
|
|
|
}
|
2018-10-12 05:51:14 +08:00
|
|
|
|
2021-12-30 08:22:37 +08:00
|
|
|
#ifdef _MSC_VER // disable bogus warning about missing definitions
|
|
|
|
#pragma warning(disable : 4661)
|
|
|
|
#endif
|
2019-04-02 01:54:22 +08:00
|
|
|
INSTANTIATE_VARIABLE_TEMPLATES
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::evaluate
|
2019-04-13 07:50:58 +08:00
|
|
|
|
|
|
|
template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;
|