xref: /llvm-project/flang/lib/Evaluate/variable.cpp (revision 4ca111d4cb4c0b425268c86b54fb19c4be2e88dd)
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 //===----------------------------------------------------------------------===//
8 
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>
20 
21 using namespace Fortran::parser::literals;
22 
23 namespace Fortran::evaluate {
24 
25 // Constructors, accessors, mutators
26 
27 Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}
28 
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));
35   }
36   if (u) {
37     upper_.emplace(std::move(*u));
38   }
39 }
40 
41 std::optional<Expr<SubscriptInteger>> Triplet::lower() const {
42   if (lower_) {
43     return {lower_.value().value()};
44   }
45   return std::nullopt;
46 }
47 
48 Triplet &Triplet::set_lower(Expr<SubscriptInteger> &&expr) {
49   lower_.emplace(std::move(expr));
50   return *this;
51 }
52 
53 std::optional<Expr<SubscriptInteger>> Triplet::upper() const {
54   if (upper_) {
55     return {upper_.value().value()};
56   }
57   return std::nullopt;
58 }
59 
60 Triplet &Triplet::set_upper(Expr<SubscriptInteger> &&expr) {
61   upper_.emplace(std::move(expr));
62   return *this;
63 }
64 
65 Expr<SubscriptInteger> Triplet::stride() const { return stride_.value(); }
66 
67 Triplet &Triplet::set_stride(Expr<SubscriptInteger> &&expr) {
68   stride_.value() = std::move(expr);
69   return *this;
70 }
71 
72 bool Triplet::IsStrideOne() const {
73   if (auto stride{ToInt64(stride_.value())}) {
74     return stride == 1;
75   } else {
76     return false;
77   }
78 }
79 
80 CoarrayRef::CoarrayRef(SymbolVector &&base, std::vector<Subscript> &&ss,
81     std::vector<Expr<SubscriptInteger>> &&css)
82     : base_{std::move(base)}, subscript_(std::move(ss)),
83       cosubscript_(std::move(css)) {
84   CHECK(!base_.empty());
85   CHECK(!cosubscript_.empty());
86 }
87 
88 std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
89   if (stat_) {
90     return stat_.value().value();
91   } else {
92     return std::nullopt;
93   }
94 }
95 
96 std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
97   if (team_) {
98     return team_.value().value();
99   } else {
100     return std::nullopt;
101   }
102 }
103 
104 CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
105   CHECK(IsVariable(v));
106   stat_.emplace(std::move(v));
107   return *this;
108 }
109 
110 CoarrayRef &CoarrayRef::set_team(Expr<SomeInteger> &&v, bool isTeamNumber) {
111   CHECK(IsVariable(v));
112   team_.emplace(std::move(v));
113   teamIsTeamNumber_ = isTeamNumber;
114   return *this;
115 }
116 
117 const Symbol &CoarrayRef::GetFirstSymbol() const { return base_.front(); }
118 
119 const Symbol &CoarrayRef::GetLastSymbol() const { return base_.back(); }
120 
121 void Substring::SetBounds(std::optional<Expr<SubscriptInteger>> &lower,
122     std::optional<Expr<SubscriptInteger>> &upper) {
123   if (lower) {
124     set_lower(std::move(lower.value()));
125   }
126   if (upper) {
127     set_upper(std::move(upper.value()));
128   }
129 }
130 
131 Expr<SubscriptInteger> Substring::lower() const {
132   if (lower_) {
133     return lower_.value().value();
134   } else {
135     return AsExpr(Constant<SubscriptInteger>{1});
136   }
137 }
138 
139 Substring &Substring::set_lower(Expr<SubscriptInteger> &&expr) {
140   lower_.emplace(std::move(expr));
141   return *this;
142 }
143 
144 std::optional<Expr<SubscriptInteger>> Substring::upper() const {
145   if (upper_) {
146     return upper_.value().value();
147   } else {
148     return std::visit(
149         common::visitors{
150             [](const DataRef &dataRef) { return dataRef.LEN(); },
151             [](const StaticDataObject::Pointer &object)
152                 -> std::optional<Expr<SubscriptInteger>> {
153               return AsExpr(Constant<SubscriptInteger>{object->data().size()});
154             },
155         },
156         parent_);
157   }
158 }
159 
160 Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {
161   upper_.emplace(std::move(expr));
162   return *this;
163 }
164 
165 std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
166   if (!upper_) {
167     upper_ = upper();
168     if (!upper_) {
169       return std::nullopt;
170     }
171   }
172   upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));
173   std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())};
174   if (!ubi) {
175     return std::nullopt;
176   }
177   if (!lower_) {
178     lower_ = AsExpr(Constant<SubscriptInteger>{1});
179   }
180   lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));
181   std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};
182   if (!lbi) {
183     return std::nullopt;
184   }
185   if (*lbi > *ubi) { // empty result; canonicalize
186     *lbi = 1;
187     *ubi = 0;
188     lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
189     upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
190   }
191   std::optional<ConstantSubscript> length;
192   std::optional<Expr<SomeCharacter>> strings; // a Constant<Character>
193   if (const auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)}) {
194     length = (*literal)->data().size();
195     if (auto str{(*literal)->AsString()}) {
196       strings =
197           Expr<SomeCharacter>(Expr<Ascii>(Constant<Ascii>{std::move(*str)}));
198     }
199   } else if (const auto *dataRef{std::get_if<DataRef>(&parent_)}) {
200     if (auto expr{AsGenericExpr(DataRef{*dataRef})}) {
201       auto folded{evaluate::Fold(context, std::move(*expr))};
202       if (IsActuallyConstant(folded)) {
203         if (const auto *value{UnwrapExpr<Expr<SomeCharacter>>(folded)}) {
204           strings = *value;
205         }
206       }
207     }
208   }
209   std::optional<Expr<SomeCharacter>> result;
210   if (strings) {
211     result = std::visit(
212         [&](const auto &expr) -> std::optional<Expr<SomeCharacter>> {
213           using Type = typename std::decay_t<decltype(expr)>::Result;
214           if (const auto *cc{std::get_if<Constant<Type>>(&expr.u)}) {
215             if (auto substr{cc->Substring(*lbi, *ubi)}) {
216               return Expr<SomeCharacter>{Expr<Type>{*substr}};
217             }
218           }
219           return std::nullopt;
220         },
221         strings->u);
222   }
223   if (!result) { // error cases
224     if (*lbi < 1) {
225       context.messages().Say(
226           "Lower bound (%jd) on substring is less than one"_warn_en_US,
227           static_cast<std::intmax_t>(*lbi));
228       *lbi = 1;
229       lower_ = AsExpr(Constant<SubscriptInteger>{1});
230     }
231     if (length && *ubi > *length) {
232       context.messages().Say(
233           "Upper bound (%jd) on substring is greater than character length (%jd)"_warn_en_US,
234           static_cast<std::intmax_t>(*ubi),
235           static_cast<std::intmax_t>(*length));
236       *ubi = *length;
237       upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
238     }
239   }
240   return result;
241 }
242 
243 DescriptorInquiry::DescriptorInquiry(
244     const NamedEntity &base, Field field, int dim)
245     : base_{base}, field_{field}, dimension_{dim} {
246   const Symbol &last{base_.GetLastSymbol()};
247   CHECK(IsDescriptor(last));
248   CHECK(((field == Field::Len || field == Field::Rank) && dim == 0) ||
249       (field != Field::Len && dim >= 0 && dim < last.Rank()));
250 }
251 
252 DescriptorInquiry::DescriptorInquiry(NamedEntity &&base, Field field, int dim)
253     : base_{std::move(base)}, field_{field}, dimension_{dim} {
254   const Symbol &last{base_.GetLastSymbol()};
255   CHECK(IsDescriptor(last));
256   CHECK((field == Field::Len && dim == 0) ||
257       (field != Field::Len && dim >= 0 && dim < last.Rank()));
258 }
259 
260 // LEN()
261 static std::optional<Expr<SubscriptInteger>> SymbolLEN(const Symbol &symbol) {
262   const Symbol &ultimate{symbol.GetUltimate()};
263   if (const auto *assoc{ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
264     if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc->expr())}) {
265       return chExpr->LEN();
266     }
267   }
268   if (auto dyType{DynamicType::From(ultimate)}) {
269     if (auto len{dyType->GetCharLength()}) {
270       if (ultimate.owner().IsDerivedType() || IsScopeInvariantExpr(*len)) {
271         return AsExpr(Extremum<SubscriptInteger>{
272             Ordering::Greater, Expr<SubscriptInteger>{0}, std::move(*len)});
273       }
274     }
275   }
276   if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
277     return Expr<SubscriptInteger>{
278         DescriptorInquiry{NamedEntity{symbol}, DescriptorInquiry::Field::Len}};
279   }
280   return std::nullopt;
281 }
282 
283 std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {
284   return std::visit(
285       common::visitors{
286           [](const Symbol &symbol) { return SymbolLEN(symbol); },
287           [](const StaticDataObject::Pointer &object)
288               -> std::optional<Expr<SubscriptInteger>> {
289             return AsExpr(Constant<SubscriptInteger>{object->data().size()});
290           },
291       },
292       u);
293 }
294 
295 std::optional<Expr<SubscriptInteger>> Component::LEN() const {
296   return SymbolLEN(GetLastSymbol());
297 }
298 
299 std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {
300   return SymbolLEN(GetLastSymbol());
301 }
302 
303 std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {
304   return base_.LEN();
305 }
306 
307 std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {
308   return SymbolLEN(GetLastSymbol());
309 }
310 
311 std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
312   return std::visit(common::visitors{
313                         [](SymbolRef symbol) { return SymbolLEN(symbol); },
314                         [](const auto &x) { return x.LEN(); },
315                     },
316       u);
317 }
318 
319 std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
320   if (auto top{upper()}) {
321     return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
322         AsExpr(Constant<SubscriptInteger>{0}),
323         *std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
324   } else {
325     return std::nullopt;
326   }
327 }
328 
329 template <typename T>
330 std::optional<Expr<SubscriptInteger>> Designator<T>::LEN() const {
331   if constexpr (T::category == TypeCategory::Character) {
332     return std::visit(common::visitors{
333                           [](SymbolRef symbol) { return SymbolLEN(symbol); },
334                           [](const auto &x) { return x.LEN(); },
335                       },
336         u);
337   } else {
338     common::die("Designator<non-char>::LEN() called");
339     return std::nullopt;
340   }
341 }
342 
343 std::optional<Expr<SubscriptInteger>> ProcedureDesignator::LEN() const {
344   using T = std::optional<Expr<SubscriptInteger>>;
345   return std::visit(
346       common::visitors{
347           [](SymbolRef symbol) -> T { return SymbolLEN(symbol); },
348           [](const common::CopyableIndirection<Component> &c) -> T {
349             return c.value().LEN();
350           },
351           [](const SpecificIntrinsic &i) -> T {
352             // Some cases whose results' lengths can be determined
353             // from the lengths of their arguments are handled in
354             // ProcedureRef::LEN() before coming here.
355             if (const auto &result{i.characteristics.value().functionResult}) {
356               if (const auto *type{result->GetTypeAndShape()}) {
357                 if (auto length{type->type().GetCharLength()}) {
358                   return std::move(*length);
359                 }
360               }
361             }
362             return std::nullopt;
363           },
364       },
365       u);
366 }
367 
368 // Rank()
369 int BaseObject::Rank() const {
370   return std::visit(common::visitors{
371                         [](SymbolRef symbol) { return symbol->Rank(); },
372                         [](const StaticDataObject::Pointer &) { return 0; },
373                     },
374       u);
375 }
376 
377 int Component::Rank() const {
378   if (int rank{symbol_->Rank()}; rank > 0) {
379     return rank;
380   }
381   return base().Rank();
382 }
383 
384 int NamedEntity::Rank() const {
385   return std::visit(common::visitors{
386                         [](const SymbolRef s) { return s->Rank(); },
387                         [](const Component &c) { return c.Rank(); },
388                     },
389       u_);
390 }
391 
392 int Subscript::Rank() const {
393   return std::visit(common::visitors{
394                         [](const IndirectSubscriptIntegerExpr &x) {
395                           return x.value().Rank();
396                         },
397                         [](const Triplet &) { return 1; },
398                     },
399       u);
400 }
401 
402 int ArrayRef::Rank() const {
403   int rank{0};
404   for (const auto &expr : subscript_) {
405     rank += expr.Rank();
406   }
407   if (rank > 0) {
408     return rank;
409   } else if (const Component * component{base_.UnwrapComponent()}) {
410     return component->base().Rank();
411   } else {
412     return 0;
413   }
414 }
415 
416 int CoarrayRef::Rank() const {
417   if (!subscript_.empty()) {
418     int rank{0};
419     for (const auto &expr : subscript_) {
420       rank += expr.Rank();
421     }
422     return rank;
423   } else {
424     return base_.back()->Rank();
425   }
426 }
427 
428 int DataRef::Rank() const {
429   return std::visit(common::visitors{
430                         [](SymbolRef symbol) { return symbol->Rank(); },
431                         [](const auto &x) { return x.Rank(); },
432                     },
433       u);
434 }
435 
436 int Substring::Rank() const {
437   return std::visit(common::visitors{
438                         [](const DataRef &dataRef) { return dataRef.Rank(); },
439                         [](const StaticDataObject::Pointer &) { return 0; },
440                     },
441       parent_);
442 }
443 
444 int ComplexPart::Rank() const { return complex_.Rank(); }
445 
446 template <typename T> int Designator<T>::Rank() const {
447   return std::visit(common::visitors{
448                         [](SymbolRef symbol) { return symbol->Rank(); },
449                         [](const auto &x) { return x.Rank(); },
450                     },
451       u);
452 }
453 
454 // GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.
455 const Symbol &Component::GetFirstSymbol() const {
456   return base_.value().GetFirstSymbol();
457 }
458 
459 const Symbol &NamedEntity::GetFirstSymbol() const {
460   return std::visit(common::visitors{
461                         [](SymbolRef s) -> const Symbol & { return s; },
462                         [](const Component &c) -> const Symbol & {
463                           return c.GetFirstSymbol();
464                         },
465                     },
466       u_);
467 }
468 
469 const Symbol &NamedEntity::GetLastSymbol() const {
470   return std::visit(common::visitors{
471                         [](SymbolRef s) -> const Symbol & { return s; },
472                         [](const Component &c) -> const Symbol & {
473                           return c.GetLastSymbol();
474                         },
475                     },
476       u_);
477 }
478 
479 const Component *NamedEntity::UnwrapComponent() const {
480   return std::visit(common::visitors{
481                         [](SymbolRef) -> const Component * { return nullptr; },
482                         [](const Component &c) { return &c; },
483                     },
484       u_);
485 }
486 
487 Component *NamedEntity::UnwrapComponent() {
488   return std::visit(common::visitors{
489                         [](SymbolRef &) -> Component * { return nullptr; },
490                         [](Component &c) { return &c; },
491                     },
492       u_);
493 }
494 
495 const Symbol &ArrayRef::GetFirstSymbol() const {
496   return base_.GetFirstSymbol();
497 }
498 
499 const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }
500 
501 const Symbol &DataRef::GetFirstSymbol() const {
502   return *std::visit(common::visitors{
503                          [](SymbolRef symbol) { return &*symbol; },
504                          [](const auto &x) { return &x.GetFirstSymbol(); },
505                      },
506       u);
507 }
508 
509 const Symbol &DataRef::GetLastSymbol() const {
510   return *std::visit(common::visitors{
511                          [](SymbolRef symbol) { return &*symbol; },
512                          [](const auto &x) { return &x.GetLastSymbol(); },
513                      },
514       u);
515 }
516 
517 BaseObject Substring::GetBaseObject() const {
518   return std::visit(common::visitors{
519                         [](const DataRef &dataRef) {
520                           return BaseObject{dataRef.GetFirstSymbol()};
521                         },
522                         [](StaticDataObject::Pointer pointer) {
523                           return BaseObject{std::move(pointer)};
524                         },
525                     },
526       parent_);
527 }
528 
529 const Symbol *Substring::GetLastSymbol() const {
530   return std::visit(
531       common::visitors{
532           [](const DataRef &dataRef) { return &dataRef.GetLastSymbol(); },
533           [](const auto &) -> const Symbol * { return nullptr; },
534       },
535       parent_);
536 }
537 
538 template <typename T> BaseObject Designator<T>::GetBaseObject() const {
539   return std::visit(
540       common::visitors{
541           [](SymbolRef symbol) { return BaseObject{symbol}; },
542           [](const Substring &sstring) { return sstring.GetBaseObject(); },
543           [](const auto &x) {
544 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
545             if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
546                               Substring>) {
547               return x.GetBaseObject();
548             } else
549 #endif
550               return BaseObject{x.GetFirstSymbol()};
551           },
552       },
553       u);
554 }
555 
556 template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
557   return std::visit(
558       common::visitors{
559           [](SymbolRef symbol) { return &*symbol; },
560           [](const Substring &sstring) { return sstring.GetLastSymbol(); },
561           [](const auto &x) {
562 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
563             if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
564                               Substring>) {
565               return x.GetLastSymbol();
566             } else
567 #endif
568               return &x.GetLastSymbol();
569           },
570       },
571       u);
572 }
573 
574 template <typename T>
575 std::optional<DynamicType> Designator<T>::GetType() const {
576   if constexpr (IsLengthlessIntrinsicType<Result>) {
577     return Result::GetType();
578   } else if (const Symbol * symbol{GetLastSymbol()}) {
579     return DynamicType::From(*symbol);
580   } else if constexpr (Result::category == TypeCategory::Character) {
581     if (const Substring * substring{std::get_if<Substring>(&u)}) {
582       const auto *parent{substring->GetParentIf<StaticDataObject::Pointer>()};
583       CHECK(parent);
584       return DynamicType{TypeCategory::Character, (*parent)->itemBytes()};
585     }
586   }
587   return std::nullopt;
588 }
589 
590 static NamedEntity AsNamedEntity(const SymbolVector &x) {
591   CHECK(!x.empty());
592   NamedEntity result{x.front()};
593   int j{0};
594   for (const Symbol &symbol : x) {
595     if (j++ != 0) {
596       DataRef base{result.IsSymbol() ? DataRef{result.GetLastSymbol()}
597                                      : DataRef{result.GetComponent()}};
598       result = NamedEntity{Component{std::move(base), symbol}};
599     }
600   }
601   return result;
602 }
603 
604 NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); }
605 
606 // Equality testing
607 
608 // For the purposes of comparing type parameter expressions while
609 // testing the compatibility of procedure characteristics, two
610 // object dummy arguments with the same name are considered equal.
611 static bool AreSameSymbol(const Symbol &x, const Symbol &y) {
612   if (&x == &y) {
613     return true;
614   }
615   if (x.name() == y.name()) {
616     if (const auto *xObject{x.detailsIf<semantics::ObjectEntityDetails>()}) {
617       if (const auto *yObject{y.detailsIf<semantics::ObjectEntityDetails>()}) {
618         return xObject->isDummy() && yObject->isDummy();
619       }
620     }
621   }
622   return false;
623 }
624 
625 // Implements operator==() for a union type, using special case handling
626 // for Symbol references.
627 template <typename A> static bool TestVariableEquality(const A &x, const A &y) {
628   const SymbolRef *xSymbol{std::get_if<SymbolRef>(&x.u)};
629   if (const SymbolRef * ySymbol{std::get_if<SymbolRef>(&y.u)}) {
630     return xSymbol && AreSameSymbol(*xSymbol, *ySymbol);
631   } else {
632     return x.u == y.u;
633   }
634 }
635 
636 bool BaseObject::operator==(const BaseObject &that) const {
637   return TestVariableEquality(*this, that);
638 }
639 bool Component::operator==(const Component &that) const {
640   return base_ == that.base_ && &*symbol_ == &*that.symbol_;
641 }
642 bool NamedEntity::operator==(const NamedEntity &that) const {
643   if (IsSymbol()) {
644     return that.IsSymbol() &&
645         AreSameSymbol(GetFirstSymbol(), that.GetFirstSymbol());
646   } else {
647     return !that.IsSymbol() && GetComponent() == that.GetComponent();
648   }
649 }
650 bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {
651   return &*parameter_ == &*that.parameter_ && base_ == that.base_;
652 }
653 bool Triplet::operator==(const Triplet &that) const {
654   return lower_ == that.lower_ && upper_ == that.upper_ &&
655       stride_ == that.stride_;
656 }
657 bool Subscript::operator==(const Subscript &that) const { return u == that.u; }
658 bool ArrayRef::operator==(const ArrayRef &that) const {
659   return base_ == that.base_ && subscript_ == that.subscript_;
660 }
661 bool CoarrayRef::operator==(const CoarrayRef &that) const {
662   return base_ == that.base_ && subscript_ == that.subscript_ &&
663       cosubscript_ == that.cosubscript_ && stat_ == that.stat_ &&
664       team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_;
665 }
666 bool DataRef::operator==(const DataRef &that) const {
667   return TestVariableEquality(*this, that);
668 }
669 bool Substring::operator==(const Substring &that) const {
670   return parent_ == that.parent_ && lower_ == that.lower_ &&
671       upper_ == that.upper_;
672 }
673 bool ComplexPart::operator==(const ComplexPart &that) const {
674   return part_ == that.part_ && complex_ == that.complex_;
675 }
676 bool ProcedureRef::operator==(const ProcedureRef &that) const {
677   return proc_ == that.proc_ && arguments_ == that.arguments_;
678 }
679 template <typename T>
680 bool Designator<T>::operator==(const Designator<T> &that) const {
681   return TestVariableEquality(*this, that);
682 }
683 bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
684   return field_ == that.field_ && base_ == that.base_ &&
685       dimension_ == that.dimension_;
686 }
687 
688 #ifdef _MSC_VER // disable bogus warning about missing definitions
689 #pragma warning(disable : 4661)
690 #endif
691 INSTANTIATE_VARIABLE_TEMPLATES
692 } // namespace Fortran::evaluate
693 
694 template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;
695