LAA: improve code in getStrideFromPointer (NFC) (#124780)
[llvm-project.git] / flang / lib / Evaluate / variable.cpp
blob841d0f71ed0e2fa2f075ec987968b89484f8ca8c
1 //===-- lib/Evaluate/variable.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "flang/Evaluate/variable.h"
10 #include "flang/Common/idioms.h"
11 #include "flang/Evaluate/check-expression.h"
12 #include "flang/Evaluate/fold.h"
13 #include "flang/Evaluate/tools.h"
14 #include "flang/Parser/char-block.h"
15 #include "flang/Parser/characters.h"
16 #include "flang/Parser/message.h"
17 #include "flang/Semantics/scope.h"
18 #include "flang/Semantics/symbol.h"
19 #include <type_traits>
21 using namespace Fortran::parser::literals;
23 namespace Fortran::evaluate {
25 // Constructors, accessors, mutators
27 Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}
29 Triplet::Triplet(std::optional<Expr<SubscriptInteger>> &&l,
30 std::optional<Expr<SubscriptInteger>> &&u,
31 std::optional<Expr<SubscriptInteger>> &&s)
32 : stride_{s ? std::move(*s) : Expr<SubscriptInteger>{1}} {
33 if (l) {
34 lower_.emplace(std::move(*l));
36 if (u) {
37 upper_.emplace(std::move(*u));
41 std::optional<Expr<SubscriptInteger>> Triplet::lower() const {
42 if (lower_) {
43 return {lower_.value().value()};
45 return std::nullopt;
48 Triplet &Triplet::set_lower(Expr<SubscriptInteger> &&expr) {
49 lower_.emplace(std::move(expr));
50 return *this;
53 std::optional<Expr<SubscriptInteger>> Triplet::upper() const {
54 if (upper_) {
55 return {upper_.value().value()};
57 return std::nullopt;
60 Triplet &Triplet::set_upper(Expr<SubscriptInteger> &&expr) {
61 upper_.emplace(std::move(expr));
62 return *this;
65 Expr<SubscriptInteger> Triplet::stride() const { return stride_.value(); }
67 Triplet &Triplet::set_stride(Expr<SubscriptInteger> &&expr) {
68 stride_.value() = std::move(expr);
69 return *this;
72 CoarrayRef::CoarrayRef(SymbolVector &&base, std::vector<Subscript> &&ss,
73 std::vector<Expr<SubscriptInteger>> &&css)
74 : base_{std::move(base)}, subscript_(std::move(ss)),
75 cosubscript_(std::move(css)) {
76 CHECK(!base_.empty());
77 CHECK(!cosubscript_.empty());
80 std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
81 if (stat_) {
82 return stat_.value().value();
83 } else {
84 return std::nullopt;
88 std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
89 if (team_) {
90 return team_.value().value();
91 } else {
92 return std::nullopt;
96 CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
97 CHECK(IsVariable(v));
98 stat_.emplace(std::move(v));
99 return *this;
102 CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&v, bool isTeamNumber) {
103 CHECK(IsVariable(v));
104 team_.emplace(std::move(v));
105 teamIsTeamNumber_ = isTeamNumber;
106 return *this;
109 const Symbol &CoarrayRef::GetFirstSymbol() const { return base_.front(); }
111 const Symbol &CoarrayRef::GetLastSymbol() const { return base_.back(); }
113 void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &lower,
114 std::optional<Expr<SubscriptInteger>> &upper) {
115 if (lower) {
116 set_lower(std::move(lower.value()));
118 if (upper) {
119 set_upper(std::move(upper.value()));
123 Expr<SubscriptInteger> Substring::lower() const {
124 if (lower_) {
125 return lower_.value().value();
126 } else {
127 return AsExpr(Constant<SubscriptInteger>{1});
131 Substring &Substring::set_lower(Expr<SubscriptInteger> &&expr) {
132 lower_.emplace(std::move(expr));
133 return *this;
136 std::optional<Expr<SubscriptInteger>> Substring::upper() const {
137 if (upper_) {
138 return upper_.value().value();
139 } else {
140 return common::visit(
141 common::visitors{
142 [](const DataRef &dataRef) { return dataRef.LEN(); },
143 [](const StaticDataObject::Pointer &object)
144 -> std::optional<Expr<SubscriptInteger>> {
145 return AsExpr(Constant<SubscriptInteger>{object->data().size()});
148 parent_);
152 Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {
153 upper_.emplace(std::move(expr));
154 return *this;
157 std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
158 if (!upper_) {
159 upper_ = upper();
160 if (!upper_) {
161 return std::nullopt;
164 upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));
165 std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())};
166 if (!ubi) {
167 return std::nullopt;
169 if (!lower_) {
170 lower_ = AsExpr(Constant<SubscriptInteger>{1});
172 lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));
173 std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};
174 if (!lbi) {
175 return std::nullopt;
177 if (*lbi > *ubi) { // empty result; canonicalize
178 *lbi = 1;
179 *ubi = 0;
180 lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
181 upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
183 std::optional<ConstantSubscript> length;
184 std::optional<Expr<SomeCharacter>> strings; // a Constant<Character>
185 if (const auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)}) {
186 length = (*literal)->data().size();
187 if (auto str{(*literal)->AsString()}) {
188 strings =
189 Expr<SomeCharacter>(Expr<Ascii>(Constant<Ascii>{std::move(*str)}));
191 } else if (const auto *dataRef{std::get_if<DataRef>(&parent_)}) {
192 if (auto expr{AsGenericExpr(DataRef{*dataRef})}) {
193 auto folded{evaluate::Fold(context, std::move(*expr))};
194 if (IsActuallyConstant(folded)) {
195 if (const auto *value{UnwrapExpr<Expr<SomeCharacter>>(folded)}) {
196 strings = *value;
201 std::optional<Expr<SomeCharacter>> result;
202 if (strings) {
203 result = common::visit(
204 [&](const auto &expr) -> std::optional<Expr<SomeCharacter>> {
205 using Type = typename std::decay_t<decltype(expr)>::Result;
206 if (const auto *cc{std::get_if<Constant<Type>>(&expr.u)}) {
207 if (auto substr{cc->Substring(*lbi, *ubi)}) {
208 return Expr<SomeCharacter>{Expr<Type>{*substr}};
211 return std::nullopt;
213 strings->u);
215 if (!result) { // error cases
216 if (*lbi < 1) {
217 if (context.languageFeatures().ShouldWarn(common::UsageWarning::Bounds)) {
218 context.messages().Say(common::UsageWarning::Bounds,
219 "Lower bound (%jd) on substring is less than one"_warn_en_US,
220 static_cast<std::intmax_t>(*lbi));
222 *lbi = 1;
223 lower_ = AsExpr(Constant<SubscriptInteger>{1});
225 if (length && *ubi > *length) {
226 if (context.languageFeatures().ShouldWarn(common::UsageWarning::Bounds)) {
227 context.messages().Say(common::UsageWarning::Bounds,
228 "Upper bound (%jd) on substring is greater than character length (%jd)"_warn_en_US,
229 static_cast<std::intmax_t>(*ubi),
230 static_cast<std::intmax_t>(*length));
232 *ubi = *length;
233 upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
236 return result;
239 DescriptorInquiry::DescriptorInquiry(
240 const NamedEntity &base, Field field, int dim)
241 : base_{base}, field_{field}, dimension_{dim} {
242 const Symbol &last{base_.GetLastSymbol()};
243 CHECK(IsDescriptor(last));
244 CHECK(((field == Field::Len || field == Field::Rank) && dim == 0) ||
245 (field != Field::Len && dim >= 0 && dim < last.Rank()));
248 DescriptorInquiry::DescriptorInquiry(NamedEntity &&base, Field field, int dim)
249 : base_{std::move(base)}, field_{field}, dimension_{dim} {
250 const Symbol &last{base_.GetLastSymbol()};
251 CHECK(IsDescriptor(last));
252 CHECK((field == Field::Len && dim == 0) ||
253 (field != Field::Len && dim >= 0 &&
254 (dim < last.Rank() || IsAssumedRank(last))));
257 // LEN()
258 static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &symbol) {
259 const Symbol &ultimate{symbol.GetUltimate()};
260 if (const auto *assoc{ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
261 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc->expr())}) {
262 return chExpr->LEN();
265 if (auto dyType{DynamicType::From(ultimate)}) {
266 auto len{dyType->GetCharLength()};
267 if (!len && ultimate.attrs().test(semantics::Attr::PARAMETER)) {
268 // Its initializer determines the length of an implied-length named
269 // constant.
270 if (const auto *object{
271 ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
272 if (object->init()) {
273 if (auto dyType2{DynamicType::From(*object->init())}) {
274 len = dyType2->GetCharLength();
279 if (len) {
280 if (auto constLen{ToInt64(*len)}) {
281 return Expr<SubscriptInteger>{std::max<std::int64_t>(*constLen, 0)};
282 } else if (ultimate.owner().IsDerivedType() ||
283 IsScopeInvariantExpr(*len)) {
284 return AsExpr(Extremum<SubscriptInteger>{
285 Ordering::Greater, Expr<SubscriptInteger>{0}, std::move(*len)});
289 if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
290 return Expr<SubscriptInteger>{
291 DescriptorInquiry{NamedEntity{symbol}, DescriptorInquiry::Field::Len}};
293 return std::nullopt;
296 std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {
297 return common::visit(
298 common::visitors{
299 [](const Symbol &symbol) { return SymbolLEN(symbol); },
300 [](const StaticDataObject::Pointer &object)
301 -> std::optional<Expr<SubscriptInteger>> {
302 return AsExpr(Constant<SubscriptInteger>{object->data().size()});
308 std::optional<Expr<SubscriptInteger>> Component::LEN() const {
309 return SymbolLEN(GetLastSymbol());
312 std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {
313 return SymbolLEN(GetLastSymbol());
316 std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {
317 return base_.LEN();
320 std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {
321 return SymbolLEN(GetLastSymbol());
324 std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
325 return common::visit(common::visitors{
326 [](SymbolRef symbol) { return SymbolLEN(symbol); },
327 [](const auto &x) { return x.LEN(); },
332 std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
333 if (auto top{upper()}) {
334 return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
335 AsExpr(Constant<SubscriptInteger>{0}),
336 *std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
337 } else {
338 return std::nullopt;
342 template <typename T>
343 std::optional<Expr<SubscriptInteger>> Designator<T>::LEN() const {
344 if constexpr (T::category == TypeCategory::Character) {
345 return common::visit(common::visitors{
346 [](SymbolRef symbol) { return SymbolLEN(symbol); },
347 [](const auto &x) { return x.LEN(); },
350 } else {
351 common::die("Designator<non-char>::LEN() called");
352 return std::nullopt;
356 std::optional<Expr<SubscriptInteger>> ProcedureDesignator::LEN() const {
357 using T = std::optional<Expr<SubscriptInteger>>;
358 return common::visit(
359 common::visitors{
360 [](SymbolRef symbol) -> T { return SymbolLEN(symbol); },
361 [](const common::CopyableIndirection<Component> &c) -> T {
362 return c.value().LEN();
364 [](const SpecificIntrinsic &i) -> T {
365 // Some cases whose results' lengths can be determined
366 // from the lengths of their arguments are handled in
367 // ProcedureRef::LEN() before coming here.
368 if (const auto &result{i.characteristics.value().functionResult}) {
369 if (const auto *type{result->GetTypeAndShape()}) {
370 if (auto length{type->type().GetCharLength()}) {
371 return std::move(*length);
375 return std::nullopt;
381 // Rank()
382 int BaseObject::Rank() const {
383 return common::visit(common::visitors{
384 [](SymbolRef symbol) { return symbol->Rank(); },
385 [](const StaticDataObject::Pointer &) { return 0; },
390 int Component::Rank() const {
391 if (int rank{symbol_->Rank()}; rank > 0) {
392 return rank;
394 return base().Rank();
397 int NamedEntity::Rank() const {
398 return common::visit(common::visitors{
399 [](const SymbolRef s) { return s->Rank(); },
400 [](const Component &c) { return c.Rank(); },
402 u_);
405 int Subscript::Rank() const {
406 return common::visit(common::visitors{
407 [](const IndirectSubscriptIntegerExpr &x) {
408 return x.value().Rank();
410 [](const Triplet &) { return 1; },
415 int ArrayRef::Rank() const {
416 int rank{0};
417 for (const auto &expr : subscript_) {
418 rank += expr.Rank();
420 if (rank > 0) {
421 return rank;
422 } else if (const Component * component{base_.UnwrapComponent()}) {
423 return component->base().Rank();
424 } else {
425 return 0;
429 int CoarrayRef::Rank() const {
430 if (!subscript_.empty()) {
431 int rank{0};
432 for (const auto &expr : subscript_) {
433 rank += expr.Rank();
435 return rank;
436 } else {
437 return base_.back()->Rank();
441 int DataRef::Rank() const {
442 return common::visit(common::visitors{
443 [](SymbolRef symbol) { return symbol->Rank(); },
444 [](const auto &x) { return x.Rank(); },
449 int Substring::Rank() const {
450 return common::visit(
451 common::visitors{
452 [](const DataRef &dataRef) { return dataRef.Rank(); },
453 [](const StaticDataObject::Pointer &) { return 0; },
455 parent_);
458 int ComplexPart::Rank() const { return complex_.Rank(); }
460 template <typename T> int Designator<T>::Rank() const {
461 return common::visit(common::visitors{
462 [](SymbolRef symbol) { return symbol->Rank(); },
463 [](const auto &x) { return x.Rank(); },
468 // Corank()
469 int BaseObject::Corank() const {
470 return common::visit(common::visitors{
471 [](SymbolRef symbol) { return symbol->Corank(); },
472 [](const StaticDataObject::Pointer &) { return 0; },
477 int Component::Corank() const {
478 if (int corank{symbol_->Corank()}; corank > 0) {
479 return corank;
481 return base().Corank();
484 int NamedEntity::Corank() const {
485 return common::visit(common::visitors{
486 [](const SymbolRef s) { return s->Corank(); },
487 [](const Component &c) { return c.Corank(); },
489 u_);
492 int ArrayRef::Corank() const { return base().Corank(); }
494 int DataRef::Corank() const {
495 return common::visit(common::visitors{
496 [](SymbolRef symbol) { return symbol->Corank(); },
497 [](const auto &x) { return x.Corank(); },
502 int Substring::Corank() const {
503 return common::visit(
504 common::visitors{
505 [](const DataRef &dataRef) { return dataRef.Corank(); },
506 [](const StaticDataObject::Pointer &) { return 0; },
508 parent_);
511 int ComplexPart::Corank() const { return complex_.Corank(); }
513 template <typename T> int Designator<T>::Corank() const {
514 return common::visit(common::visitors{
515 [](SymbolRef symbol) { return symbol->Corank(); },
516 [](const auto &x) { return x.Corank(); },
521 // GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.
522 const Symbol &Component::GetFirstSymbol() const {
523 return base_.value().GetFirstSymbol();
526 const Symbol &NamedEntity::GetFirstSymbol() const {
527 return common::visit(common::visitors{
528 [](SymbolRef s) -> const Symbol & { return s; },
529 [](const Component &c) -> const Symbol & {
530 return c.GetFirstSymbol();
533 u_);
536 const Symbol &NamedEntity::GetLastSymbol() const {
537 return common::visit(common::visitors{
538 [](SymbolRef s) -> const Symbol & { return s; },
539 [](const Component &c) -> const Symbol & {
540 return c.GetLastSymbol();
543 u_);
546 const SymbolRef *NamedEntity::UnwrapSymbolRef() const {
547 return common::visit(
548 common::visitors{
549 [](const SymbolRef &s) { return &s; },
550 [](const Component &) -> const SymbolRef * { return nullptr; },
552 u_);
555 SymbolRef *NamedEntity::UnwrapSymbolRef() {
556 return common::visit(common::visitors{
557 [](SymbolRef &s) { return &s; },
558 [](Component &) -> SymbolRef * { return nullptr; },
560 u_);
563 const Component *NamedEntity::UnwrapComponent() const {
564 return common::visit(
565 common::visitors{
566 [](SymbolRef) -> const Component * { return nullptr; },
567 [](const Component &c) { return &c; },
569 u_);
572 Component *NamedEntity::UnwrapComponent() {
573 return common::visit(common::visitors{
574 [](SymbolRef &) -> Component * { return nullptr; },
575 [](Component &c) { return &c; },
577 u_);
580 const Symbol &ArrayRef::GetFirstSymbol() const {
581 return base_.GetFirstSymbol();
584 const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }
586 const Symbol &DataRef::GetFirstSymbol() const {
587 return *common::visit(common::visitors{
588 [](SymbolRef symbol) { return &*symbol; },
589 [](const auto &x) { return &x.GetFirstSymbol(); },
594 const Symbol &DataRef::GetLastSymbol() const {
595 return *common::visit(common::visitors{
596 [](SymbolRef symbol) { return &*symbol; },
597 [](const auto &x) { return &x.GetLastSymbol(); },
602 BaseObject Substring::GetBaseObject() const {
603 return common::visit(common::visitors{
604 [](const DataRef &dataRef) {
605 return BaseObject{dataRef.GetFirstSymbol()};
607 [](StaticDataObject::Pointer pointer) {
608 return BaseObject{std::move(pointer)};
611 parent_);
614 const Symbol *Substring::GetLastSymbol() const {
615 return common::visit(
616 common::visitors{
617 [](const DataRef &dataRef) { return &dataRef.GetLastSymbol(); },
618 [](const auto &) -> const Symbol * { return nullptr; },
620 parent_);
623 template <typename T> BaseObject Designator<T>::GetBaseObject() const {
624 return common::visit(
625 common::visitors{
626 [](SymbolRef symbol) { return BaseObject{symbol}; },
627 [](const Substring &sstring) { return sstring.GetBaseObject(); },
628 [](const auto &x) { return BaseObject{x.GetFirstSymbol()}; },
633 template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
634 return common::visit(
635 common::visitors{
636 [](SymbolRef symbol) { return &*symbol; },
637 [](const Substring &sstring) { return sstring.GetLastSymbol(); },
638 [](const auto &x) { return &x.GetLastSymbol(); },
643 template <typename T>
644 std::optional<DynamicType> Designator<T>::GetType() const {
645 if constexpr (IsLengthlessIntrinsicType<Result>) {
646 return Result::GetType();
648 if constexpr (Result::category == TypeCategory::Character) {
649 if (std::holds_alternative<Substring>(u)) {
650 if (auto len{LEN()}) {
651 if (auto n{ToInt64(*len)}) {
652 return DynamicType{T::kind, *n};
655 return DynamicType{TypeCategory::Character, T::kind};
658 if (const Symbol * symbol{GetLastSymbol()}) {
659 return DynamicType::From(*symbol);
661 return std::nullopt;
664 static NamedEntity AsNamedEntity(const SymbolVector &x) {
665 CHECK(!x.empty());
666 NamedEntity result{x.front()};
667 int j{0};
668 for (const Symbol &symbol : x) {
669 if (j++ != 0) {
670 DataRef base{result.IsSymbol() ? DataRef{result.GetLastSymbol()}
671 : DataRef{result.GetComponent()}};
672 result = NamedEntity{Component{std::move(base), symbol}};
675 return result;
678 NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); }
680 // Equality testing
682 // For the purposes of comparing type parameter expressions while
683 // testing the compatibility of procedure characteristics, two
684 // dummy arguments with the same position are considered equal.
685 static std::optional<int> GetDummyArgPosition(const Symbol &original) {
686 const Symbol &symbol(original.GetUltimate());
687 if (IsDummy(symbol)) {
688 if (const Symbol * proc{symbol.owner().symbol()}) {
689 if (const auto *subp{proc->detailsIf<semantics::SubprogramDetails>()}) {
690 int j{0};
691 for (const Symbol *arg : subp->dummyArgs()) {
692 if (arg == &symbol) {
693 return j;
695 ++j;
700 return std::nullopt;
703 static bool AreSameSymbol(const Symbol &x, const Symbol &y) {
704 if (&x == &y) {
705 return true;
707 if (auto xPos{GetDummyArgPosition(x)}) {
708 if (auto yPos{GetDummyArgPosition(y)}) {
709 return *xPos == *yPos;
712 return false;
715 // Implements operator==() for a union type, using special case handling
716 // for Symbol references.
717 template <typename A> static bool TestVariableEquality(const A &x, const A &y) {
718 const SymbolRef *xSymbol{std::get_if<SymbolRef>(&x.u)};
719 if (const SymbolRef * ySymbol{std::get_if<SymbolRef>(&y.u)}) {
720 return xSymbol && AreSameSymbol(*xSymbol, *ySymbol);
721 } else {
722 return x.u == y.u;
726 bool BaseObject::operator==(const BaseObject &that) const {
727 return TestVariableEquality(*this, that);
729 bool Component::operator==(const Component &that) const {
730 return base_ == that.base_ && &*symbol_ == &*that.symbol_;
732 bool NamedEntity::operator==(const NamedEntity &that) const {
733 if (IsSymbol()) {
734 return that.IsSymbol() &&
735 AreSameSymbol(GetFirstSymbol(), that.GetFirstSymbol());
736 } else {
737 return !that.IsSymbol() && GetComponent() == that.GetComponent();
740 bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {
741 return &*parameter_ == &*that.parameter_ && base_ == that.base_;
743 bool Triplet::operator==(const Triplet &that) const {
744 return lower_ == that.lower_ && upper_ == that.upper_ &&
745 stride_ == that.stride_;
747 bool Subscript::operator==(const Subscript &that) const { return u == that.u; }
748 bool ArrayRef::operator==(const ArrayRef &that) const {
749 return base_ == that.base_ && subscript_ == that.subscript_;
751 bool CoarrayRef::operator==(const CoarrayRef &that) const {
752 return base_ == that.base_ && subscript_ == that.subscript_ &&
753 cosubscript_ == that.cosubscript_ && stat_ == that.stat_ &&
754 team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_;
756 bool DataRef::operator==(const DataRef &that) const {
757 return TestVariableEquality(*this, that);
759 bool Substring::operator==(const Substring &that) const {
760 return parent_ == that.parent_ && lower_ == that.lower_ &&
761 upper_ == that.upper_;
763 bool ComplexPart::operator==(const ComplexPart &that) const {
764 return part_ == that.part_ && complex_ == that.complex_;
766 bool ProcedureRef::operator==(const ProcedureRef &that) const {
767 return proc_ == that.proc_ && arguments_ == that.arguments_;
769 template <typename T>
770 bool Designator<T>::operator==(const Designator<T> &that) const {
771 return TestVariableEquality(*this, that);
773 bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
774 return field_ == that.field_ && base_ == that.base_ &&
775 dimension_ == that.dimension_;
778 #ifdef _MSC_VER // disable bogus warning about missing definitions
779 #pragma warning(disable : 4661)
780 #endif
781 INSTANTIATE_VARIABLE_TEMPLATES
782 } // namespace Fortran::evaluate
784 template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;