xref: /llvm-project/flang/lib/Evaluate/variable.cpp (revision 1a4af2e45ee59cff3d577986b9b7f3f5bd5ab01f)
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 (!lower_) {
167     lower_ = AsExpr(Constant<SubscriptInteger>{1});
168   }
169   lower_.value() = evaluate::Fold(context, std::move(lower_.value().value()));
170   std::optional<ConstantSubscript> lbi{ToInt64(lower_.value().value())};
171   if (lbi && *lbi < 1) {
172     context.messages().Say(
173         "Lower bound (%jd) on substring is less than one"_en_US, *lbi);
174     *lbi = 1;
175     lower_ = AsExpr(Constant<SubscriptInteger>{1});
176   }
177   if (!upper_) {
178     upper_ = upper();
179     if (!upper_) {
180       return std::nullopt;
181     }
182   }
183   upper_.value() = evaluate::Fold(context, std::move(upper_.value().value()));
184   if (std::optional<ConstantSubscript> ubi{ToInt64(upper_.value().value())}) {
185     auto *literal{std::get_if<StaticDataObject::Pointer>(&parent_)};
186     std::optional<ConstantSubscript> length;
187     if (literal) {
188       length = (*literal)->data().size();
189     } else if (const Symbol * symbol{GetLastSymbol()}) {
190       if (const semantics::DeclTypeSpec * type{symbol->GetType()}) {
191         if (type->category() == semantics::DeclTypeSpec::Character) {
192           length = ToInt64(type->characterTypeSpec().length().GetExplicit());
193         }
194       }
195     }
196     if (*ubi < 1 || (lbi && *ubi < *lbi)) {
197       // Zero-length string: canonicalize
198       *lbi = 1, *ubi = 0;
199       lower_ = AsExpr(Constant<SubscriptInteger>{*lbi});
200       upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
201     } else if (length && *ubi > *length) {
202       context.messages().Say("Upper bound (%jd) on substring is greater "
203                              "than character length (%jd)"_en_US,
204           *ubi, *length);
205       *ubi = *length;
206     }
207     if (lbi && literal) {
208       auto newStaticData{StaticDataObject::Create()};
209       auto items{0}; // If the lower bound is greater, the length is 0
210       if (*ubi >= *lbi) {
211         items = *ubi - *lbi + 1;
212       }
213       auto width{(*literal)->itemBytes()};
214       auto bytes{items * width};
215       auto startByte{(*lbi - 1) * width};
216       const auto *from{&(*literal)->data()[0] + startByte};
217       for (auto j{0}; j < bytes; ++j) {
218         newStaticData->data().push_back(from[j]);
219       }
220       parent_ = newStaticData;
221       lower_ = AsExpr(Constant<SubscriptInteger>{1});
222       ConstantSubscript length = newStaticData->data().size();
223       upper_ = AsExpr(Constant<SubscriptInteger>{length});
224       switch (width) {
225       case 1:
226         return {
227             AsCategoryExpr(AsExpr(Constant<Type<TypeCategory::Character, 1>>{
228                 *newStaticData->AsString()}))};
229       case 2:
230         return {AsCategoryExpr(Constant<Type<TypeCategory::Character, 2>>{
231             *newStaticData->AsU16String()})};
232       case 4:
233         return {AsCategoryExpr(Constant<Type<TypeCategory::Character, 4>>{
234             *newStaticData->AsU32String()})};
235       default:
236         CRASH_NO_CASE;
237       }
238     }
239   }
240   return std::nullopt;
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 && 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   } else if (auto dyType{DynamicType::From(ultimate)}) {
268     if (auto len{dyType->GetCharLength()}) {
269       return len;
270     } else if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
271       return Expr<SubscriptInteger>{DescriptorInquiry{
272           NamedEntity{symbol}, DescriptorInquiry::Field::Len}};
273     }
274   }
275   return std::nullopt;
276 }
277 
278 std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {
279   return std::visit(
280       common::visitors{
281           [](const Symbol &symbol) { return SymbolLEN(symbol); },
282           [](const StaticDataObject::Pointer &object)
283               -> std::optional<Expr<SubscriptInteger>> {
284             return AsExpr(Constant<SubscriptInteger>{object->data().size()});
285           },
286       },
287       u);
288 }
289 
290 std::optional<Expr<SubscriptInteger>> Component::LEN() const {
291   return SymbolLEN(GetLastSymbol());
292 }
293 
294 std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {
295   return SymbolLEN(GetLastSymbol());
296 }
297 
298 std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {
299   return base_.LEN();
300 }
301 
302 std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {
303   return SymbolLEN(GetLastSymbol());
304 }
305 
306 std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
307   return std::visit(common::visitors{
308                         [](SymbolRef symbol) { return SymbolLEN(symbol); },
309                         [](const auto &x) { return x.LEN(); },
310                     },
311       u);
312 }
313 
314 std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
315   if (auto top{upper()}) {
316     return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
317         AsExpr(Constant<SubscriptInteger>{0}),
318         *std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
319   } else {
320     return std::nullopt;
321   }
322 }
323 
324 template <typename T>
325 std::optional<Expr<SubscriptInteger>> Designator<T>::LEN() const {
326   if constexpr (T::category == TypeCategory::Character) {
327     return std::visit(common::visitors{
328                           [](SymbolRef symbol) { return SymbolLEN(symbol); },
329                           [](const auto &x) { return x.LEN(); },
330                       },
331         u);
332   } else {
333     common::die("Designator<non-char>::LEN() called");
334     return std::nullopt;
335   }
336 }
337 
338 std::optional<Expr<SubscriptInteger>> ProcedureDesignator::LEN() const {
339   using T = std::optional<Expr<SubscriptInteger>>;
340   return std::visit(
341       common::visitors{
342           [](SymbolRef symbol) -> T { return SymbolLEN(symbol); },
343           [](const common::CopyableIndirection<Component> &c) -> T {
344             return c.value().LEN();
345           },
346           [](const SpecificIntrinsic &i) -> T {
347             // Some cases whose results' lengths can be determined
348             // from the lengths of their arguments are handled in
349             // ProcedureRef::LEN() before coming here.
350             if (const auto &result{i.characteristics.value().functionResult}) {
351               if (const auto *type{result->GetTypeAndShape()}) {
352                 if (auto length{type->type().GetCharLength()}) {
353                   return std::move(*length);
354                 }
355               }
356             }
357             return std::nullopt;
358           },
359       },
360       u);
361 }
362 
363 // Rank()
364 int BaseObject::Rank() const {
365   return std::visit(common::visitors{
366                         [](SymbolRef symbol) { return symbol->Rank(); },
367                         [](const StaticDataObject::Pointer &) { return 0; },
368                     },
369       u);
370 }
371 
372 int Component::Rank() const {
373   if (int rank{symbol_->Rank()}; rank > 0) {
374     return rank;
375   }
376   return base().Rank();
377 }
378 
379 int NamedEntity::Rank() const {
380   return std::visit(common::visitors{
381                         [](const SymbolRef s) { return s->Rank(); },
382                         [](const Component &c) { return c.Rank(); },
383                     },
384       u_);
385 }
386 
387 int Subscript::Rank() const {
388   return std::visit(common::visitors{
389                         [](const IndirectSubscriptIntegerExpr &x) {
390                           return x.value().Rank();
391                         },
392                         [](const Triplet &) { return 1; },
393                     },
394       u);
395 }
396 
397 int ArrayRef::Rank() const {
398   int rank{0};
399   for (const auto &expr : subscript_) {
400     rank += expr.Rank();
401   }
402   if (rank > 0) {
403     return rank;
404   } else if (const Component * component{base_.UnwrapComponent()}) {
405     return component->base().Rank();
406   } else {
407     return 0;
408   }
409 }
410 
411 int CoarrayRef::Rank() const {
412   if (!subscript_.empty()) {
413     int rank{0};
414     for (const auto &expr : subscript_) {
415       rank += expr.Rank();
416     }
417     return rank;
418   } else {
419     return base_.back()->Rank();
420   }
421 }
422 
423 int DataRef::Rank() const {
424   return std::visit(common::visitors{
425                         [](SymbolRef symbol) { return symbol->Rank(); },
426                         [](const auto &x) { return x.Rank(); },
427                     },
428       u);
429 }
430 
431 int Substring::Rank() const {
432   return std::visit(common::visitors{
433                         [](const DataRef &dataRef) { return dataRef.Rank(); },
434                         [](const StaticDataObject::Pointer &) { return 0; },
435                     },
436       parent_);
437 }
438 
439 int ComplexPart::Rank() const { return complex_.Rank(); }
440 
441 template <typename T> int Designator<T>::Rank() const {
442   return std::visit(common::visitors{
443                         [](SymbolRef symbol) { return symbol->Rank(); },
444                         [](const auto &x) { return x.Rank(); },
445                     },
446       u);
447 }
448 
449 // GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.
450 const Symbol &Component::GetFirstSymbol() const {
451   return base_.value().GetFirstSymbol();
452 }
453 
454 const Symbol &NamedEntity::GetFirstSymbol() const {
455   return std::visit(common::visitors{
456                         [](SymbolRef s) -> const Symbol & { return s; },
457                         [](const Component &c) -> const Symbol & {
458                           return c.GetFirstSymbol();
459                         },
460                     },
461       u_);
462 }
463 
464 const Symbol &NamedEntity::GetLastSymbol() const {
465   return std::visit(common::visitors{
466                         [](SymbolRef s) -> const Symbol & { return s; },
467                         [](const Component &c) -> const Symbol & {
468                           return c.GetLastSymbol();
469                         },
470                     },
471       u_);
472 }
473 
474 const Component *NamedEntity::UnwrapComponent() const {
475   return std::visit(common::visitors{
476                         [](SymbolRef) -> const Component * { return nullptr; },
477                         [](const Component &c) { return &c; },
478                     },
479       u_);
480 }
481 
482 Component *NamedEntity::UnwrapComponent() {
483   return std::visit(common::visitors{
484                         [](SymbolRef &) -> Component * { return nullptr; },
485                         [](Component &c) { return &c; },
486                     },
487       u_);
488 }
489 
490 const Symbol &ArrayRef::GetFirstSymbol() const {
491   return base_.GetFirstSymbol();
492 }
493 
494 const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }
495 
496 const Symbol &DataRef::GetFirstSymbol() const {
497   return *std::visit(common::visitors{
498                          [](SymbolRef symbol) { return &*symbol; },
499                          [](const auto &x) { return &x.GetFirstSymbol(); },
500                      },
501       u);
502 }
503 
504 const Symbol &DataRef::GetLastSymbol() const {
505   return *std::visit(common::visitors{
506                          [](SymbolRef symbol) { return &*symbol; },
507                          [](const auto &x) { return &x.GetLastSymbol(); },
508                      },
509       u);
510 }
511 
512 BaseObject Substring::GetBaseObject() const {
513   return std::visit(common::visitors{
514                         [](const DataRef &dataRef) {
515                           return BaseObject{dataRef.GetFirstSymbol()};
516                         },
517                         [](StaticDataObject::Pointer pointer) {
518                           return BaseObject{std::move(pointer)};
519                         },
520                     },
521       parent_);
522 }
523 
524 const Symbol *Substring::GetLastSymbol() const {
525   return std::visit(
526       common::visitors{
527           [](const DataRef &dataRef) { return &dataRef.GetLastSymbol(); },
528           [](const auto &) -> const Symbol * { return nullptr; },
529       },
530       parent_);
531 }
532 
533 template <typename T> BaseObject Designator<T>::GetBaseObject() const {
534   return std::visit(
535       common::visitors{
536           [](SymbolRef symbol) { return BaseObject{symbol}; },
537           [](const Substring &sstring) { return sstring.GetBaseObject(); },
538           [](const auto &x) {
539 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
540             if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
541                               Substring>) {
542               return x.GetBaseObject();
543             } else
544 #endif
545               return BaseObject{x.GetFirstSymbol()};
546           },
547       },
548       u);
549 }
550 
551 template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
552   return std::visit(
553       common::visitors{
554           [](SymbolRef symbol) { return &*symbol; },
555           [](const Substring &sstring) { return sstring.GetLastSymbol(); },
556           [](const auto &x) {
557 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
558             if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
559                               Substring>) {
560               return x.GetLastSymbol();
561             } else
562 #endif
563               return &x.GetLastSymbol();
564           },
565       },
566       u);
567 }
568 
569 template <typename T>
570 std::optional<DynamicType> Designator<T>::GetType() const {
571   if constexpr (IsLengthlessIntrinsicType<Result>) {
572     return Result::GetType();
573   } else if (const Symbol * symbol{GetLastSymbol()}) {
574     return DynamicType::From(*symbol);
575   } else if constexpr (Result::category == TypeCategory::Character) {
576     if (const Substring * substring{std::get_if<Substring>(&u)}) {
577       const auto *parent{substring->GetParentIf<StaticDataObject::Pointer>()};
578       CHECK(parent);
579       return DynamicType{TypeCategory::Character, (*parent)->itemBytes()};
580     }
581   }
582   return std::nullopt;
583 }
584 
585 static NamedEntity AsNamedEntity(const SymbolVector &x) {
586   CHECK(!x.empty());
587   NamedEntity result{x.front()};
588   int j{0};
589   for (const Symbol &symbol : x) {
590     if (j++ != 0) {
591       DataRef base{result.IsSymbol() ? DataRef{result.GetLastSymbol()}
592                                      : DataRef{result.GetComponent()}};
593       result = NamedEntity{Component{std::move(base), symbol}};
594     }
595   }
596   return result;
597 }
598 
599 NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); }
600 
601 // Equality testing
602 
603 // For the purposes of comparing type parameter expressions while
604 // testing the compatibility of procedure characteristics, two
605 // object dummy arguments with the same name are considered equal.
606 static bool AreSameSymbol(const Symbol &x, const Symbol &y) {
607   if (&x == &y) {
608     return true;
609   }
610   if (x.name() == y.name()) {
611     if (const auto *xObject{x.detailsIf<semantics::ObjectEntityDetails>()}) {
612       if (const auto *yObject{y.detailsIf<semantics::ObjectEntityDetails>()}) {
613         return xObject->isDummy() && yObject->isDummy();
614       }
615     }
616   }
617   return false;
618 }
619 
620 // Implements operator==() for a union type, using special case handling
621 // for Symbol references.
622 template <typename A> static bool TestVariableEquality(const A &x, const A &y) {
623   const SymbolRef *xSymbol{std::get_if<SymbolRef>(&x.u)};
624   if (const SymbolRef * ySymbol{std::get_if<SymbolRef>(&y.u)}) {
625     return xSymbol && AreSameSymbol(*xSymbol, *ySymbol);
626   } else {
627     return x.u == y.u;
628   }
629 }
630 
631 bool BaseObject::operator==(const BaseObject &that) const {
632   return TestVariableEquality(*this, that);
633 }
634 bool Component::operator==(const Component &that) const {
635   return base_ == that.base_ && &*symbol_ == &*that.symbol_;
636 }
637 bool NamedEntity::operator==(const NamedEntity &that) const {
638   if (IsSymbol()) {
639     return that.IsSymbol() &&
640         AreSameSymbol(GetFirstSymbol(), that.GetFirstSymbol());
641   } else {
642     return !that.IsSymbol() && GetComponent() == that.GetComponent();
643   }
644 }
645 bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {
646   return &*parameter_ == &*that.parameter_ && base_ == that.base_;
647 }
648 bool Triplet::operator==(const Triplet &that) const {
649   return lower_ == that.lower_ && upper_ == that.upper_ &&
650       stride_ == that.stride_;
651 }
652 bool Subscript::operator==(const Subscript &that) const { return u == that.u; }
653 bool ArrayRef::operator==(const ArrayRef &that) const {
654   return base_ == that.base_ && subscript_ == that.subscript_;
655 }
656 bool CoarrayRef::operator==(const CoarrayRef &that) const {
657   return base_ == that.base_ && subscript_ == that.subscript_ &&
658       cosubscript_ == that.cosubscript_ && stat_ == that.stat_ &&
659       team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_;
660 }
661 bool DataRef::operator==(const DataRef &that) const {
662   return TestVariableEquality(*this, that);
663 }
664 bool Substring::operator==(const Substring &that) const {
665   return parent_ == that.parent_ && lower_ == that.lower_ &&
666       upper_ == that.upper_;
667 }
668 bool ComplexPart::operator==(const ComplexPart &that) const {
669   return part_ == that.part_ && complex_ == that.complex_;
670 }
671 bool ProcedureRef::operator==(const ProcedureRef &that) const {
672   return proc_ == that.proc_ && arguments_ == that.arguments_;
673 }
674 template <typename T>
675 bool Designator<T>::operator==(const Designator<T> &that) const {
676   return TestVariableEquality(*this, that);
677 }
678 bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
679   return field_ == that.field_ && base_ == that.base_ &&
680       dimension_ == that.dimension_;
681 }
682 
683 INSTANTIATE_VARIABLE_TEMPLATES
684 } // namespace Fortran::evaluate
685 
686 template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;
687