xref: /llvm-project/flang/lib/Evaluate/variable.cpp (revision 3a8a52f4a52e0c301a5f3d6acce684c7fd4a6d57)
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 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());
78 }
79 
80 std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
81   if (stat_) {
82     return stat_.value().value();
83   } else {
84     return std::nullopt;
85   }
86 }
87 
88 std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
89   if (team_) {
90     return team_.value().value();
91   } else {
92     return std::nullopt;
93   }
94 }
95 
96 CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
97   CHECK(IsVariable(v));
98   stat_.emplace(std::move(v));
99   return *this;
100 }
101 
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;
107 }
108 
109 const Symbol &CoarrayRef::GetFirstSymbol() const { return base_.front(); }
110 
111 const Symbol &CoarrayRef::GetLastSymbol() const { return base_.back(); }
112 
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()));
117   }
118   if (upper) {
119     set_upper(std::move(upper.value()));
120   }
121 }
122 
123 Expr<SubscriptInteger> Substring::lower() const {
124   if (lower_) {
125     return lower_.value().value();
126   } else {
127     return AsExpr(Constant<SubscriptInteger>{1});
128   }
129 }
130 
131 Substring &Substring::set_lower(Expr<SubscriptInteger> &&expr) {
132   lower_.emplace(std::move(expr));
133   return *this;
134 }
135 
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()});
146             },
147         },
148         parent_);
149   }
150 }
151 
152 Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {
153   upper_.emplace(std::move(expr));
154   return *this;
155 }
156 
157 std::optional<Expr<SomeCharacter>> Substring::Fold(FoldingContext &context) {
158   if (!upper_) {
159     upper_ = upper();
160     if (!upper_) {
161       return std::nullopt;
162     }
163   }
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;
168   }
169   if (!lower_) {
170     lower_ = AsExpr(Constant<SubscriptInteger>{1});
171   }
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;
176   }
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});
182   }
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)}));
190     }
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;
197         }
198       }
199     }
200   }
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}};
209             }
210           }
211           return std::nullopt;
212         },
213         strings->u);
214   }
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));
221       }
222       *lbi = 1;
223       lower_ = AsExpr(Constant<SubscriptInteger>{1});
224     }
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));
231       }
232       *ubi = *length;
233       upper_ = AsExpr(Constant<SubscriptInteger>{*ubi});
234     }
235   }
236   return result;
237 }
238 
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()));
246 }
247 
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))));
255 }
256 
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();
263     }
264   }
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();
275           }
276         }
277       }
278     }
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)});
286       }
287     }
288   }
289   if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
290     return Expr<SubscriptInteger>{
291         DescriptorInquiry{NamedEntity{symbol}, DescriptorInquiry::Field::Len}};
292   }
293   return std::nullopt;
294 }
295 
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()});
303           },
304       },
305       u);
306 }
307 
308 std::optional<Expr<SubscriptInteger>> Component::LEN() const {
309   return SymbolLEN(GetLastSymbol());
310 }
311 
312 std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {
313   return SymbolLEN(GetLastSymbol());
314 }
315 
316 std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {
317   return base_.LEN();
318 }
319 
320 std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {
321   return SymbolLEN(GetLastSymbol());
322 }
323 
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(); },
328                        },
329       u);
330 }
331 
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;
339   }
340 }
341 
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(); },
348                          },
349         u);
350   } else {
351     common::die("Designator<non-char>::LEN() called");
352     return std::nullopt;
353   }
354 }
355 
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();
363           },
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);
372                 }
373               }
374             }
375             return std::nullopt;
376           },
377       },
378       u);
379 }
380 
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; },
386                        },
387       u);
388 }
389 
390 int Component::Rank() const {
391   if (int rank{symbol_->Rank()}; rank > 0) {
392     return rank;
393   }
394   return base().Rank();
395 }
396 
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(); },
401                        },
402       u_);
403 }
404 
405 int Subscript::Rank() const {
406   return common::visit(common::visitors{
407                            [](const IndirectSubscriptIntegerExpr &x) {
408                              return x.value().Rank();
409                            },
410                            [](const Triplet &) { return 1; },
411                        },
412       u);
413 }
414 
415 int ArrayRef::Rank() const {
416   int rank{0};
417   for (const auto &expr : subscript_) {
418     rank += expr.Rank();
419   }
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;
426   }
427 }
428 
429 int CoarrayRef::Rank() const {
430   if (!subscript_.empty()) {
431     int rank{0};
432     for (const auto &expr : subscript_) {
433       rank += expr.Rank();
434     }
435     return rank;
436   } else {
437     return base_.back()->Rank();
438   }
439 }
440 
441 int DataRef::Rank() const {
442   return common::visit(common::visitors{
443                            [](SymbolRef symbol) { return symbol->Rank(); },
444                            [](const auto &x) { return x.Rank(); },
445                        },
446       u);
447 }
448 
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; },
454       },
455       parent_);
456 }
457 
458 int ComplexPart::Rank() const { return complex_.Rank(); }
459 
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(); },
464                        },
465       u);
466 }
467 
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; },
473                        },
474       u);
475 }
476 
477 int Component::Corank() const {
478   if (int corank{symbol_->Corank()}; corank > 0) {
479     return corank;
480   }
481   return base().Corank();
482 }
483 
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(); },
488                        },
489       u_);
490 }
491 
492 int ArrayRef::Corank() const { return base().Corank(); }
493 
494 int DataRef::Corank() const {
495   return common::visit(common::visitors{
496                            [](SymbolRef symbol) { return symbol->Corank(); },
497                            [](const auto &x) { return x.Corank(); },
498                        },
499       u);
500 }
501 
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; },
507       },
508       parent_);
509 }
510 
511 int ComplexPart::Corank() const { return complex_.Corank(); }
512 
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(); },
517                        },
518       u);
519 }
520 
521 // GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.
522 const Symbol &Component::GetFirstSymbol() const {
523   return base_.value().GetFirstSymbol();
524 }
525 
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();
531                            },
532                        },
533       u_);
534 }
535 
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();
541                            },
542                        },
543       u_);
544 }
545 
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; },
551       },
552       u_);
553 }
554 
555 SymbolRef *NamedEntity::UnwrapSymbolRef() {
556   return common::visit(common::visitors{
557                            [](SymbolRef &s) { return &s; },
558                            [](Component &) -> SymbolRef * { return nullptr; },
559                        },
560       u_);
561 }
562 
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; },
568       },
569       u_);
570 }
571 
572 Component *NamedEntity::UnwrapComponent() {
573   return common::visit(common::visitors{
574                            [](SymbolRef &) -> Component * { return nullptr; },
575                            [](Component &c) { return &c; },
576                        },
577       u_);
578 }
579 
580 const Symbol &ArrayRef::GetFirstSymbol() const {
581   return base_.GetFirstSymbol();
582 }
583 
584 const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }
585 
586 const Symbol &DataRef::GetFirstSymbol() const {
587   return *common::visit(common::visitors{
588                             [](SymbolRef symbol) { return &*symbol; },
589                             [](const auto &x) { return &x.GetFirstSymbol(); },
590                         },
591       u);
592 }
593 
594 const Symbol &DataRef::GetLastSymbol() const {
595   return *common::visit(common::visitors{
596                             [](SymbolRef symbol) { return &*symbol; },
597                             [](const auto &x) { return &x.GetLastSymbol(); },
598                         },
599       u);
600 }
601 
602 BaseObject Substring::GetBaseObject() const {
603   return common::visit(common::visitors{
604                            [](const DataRef &dataRef) {
605                              return BaseObject{dataRef.GetFirstSymbol()};
606                            },
607                            [](StaticDataObject::Pointer pointer) {
608                              return BaseObject{std::move(pointer)};
609                            },
610                        },
611       parent_);
612 }
613 
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; },
619       },
620       parent_);
621 }
622 
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()}; },
629       },
630       u);
631 }
632 
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(); },
639       },
640       u);
641 }
642 
643 template <typename T>
644 std::optional<DynamicType> Designator<T>::GetType() const {
645   if constexpr (IsLengthlessIntrinsicType<Result>) {
646     return Result::GetType();
647   }
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};
653         }
654       }
655       return DynamicType{TypeCategory::Character, T::kind};
656     }
657   }
658   if (const Symbol * symbol{GetLastSymbol()}) {
659     return DynamicType::From(*symbol);
660   }
661   return std::nullopt;
662 }
663 
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}};
673     }
674   }
675   return result;
676 }
677 
678 NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); }
679 
680 // Equality testing
681 
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;
694           }
695           ++j;
696         }
697       }
698     }
699   }
700   return std::nullopt;
701 }
702 
703 static bool AreSameSymbol(const Symbol &x, const Symbol &y) {
704   if (&x == &y) {
705     return true;
706   }
707   if (auto xPos{GetDummyArgPosition(x)}) {
708     if (auto yPos{GetDummyArgPosition(y)}) {
709       return *xPos == *yPos;
710     }
711   }
712   return false;
713 }
714 
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;
723   }
724 }
725 
726 bool BaseObject::operator==(const BaseObject &that) const {
727   return TestVariableEquality(*this, that);
728 }
729 bool Component::operator==(const Component &that) const {
730   return base_ == that.base_ && &*symbol_ == &*that.symbol_;
731 }
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();
738   }
739 }
740 bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {
741   return &*parameter_ == &*that.parameter_ && base_ == that.base_;
742 }
743 bool Triplet::operator==(const Triplet &that) const {
744   return lower_ == that.lower_ && upper_ == that.upper_ &&
745       stride_ == that.stride_;
746 }
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_;
750 }
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_;
755 }
756 bool DataRef::operator==(const DataRef &that) const {
757   return TestVariableEquality(*this, that);
758 }
759 bool Substring::operator==(const Substring &that) const {
760   return parent_ == that.parent_ && lower_ == that.lower_ &&
761       upper_ == that.upper_;
762 }
763 bool ComplexPart::operator==(const ComplexPart &that) const {
764   return part_ == that.part_ && complex_ == that.complex_;
765 }
766 bool ProcedureRef::operator==(const ProcedureRef &that) const {
767   return proc_ == that.proc_ && arguments_ == that.arguments_;
768 }
769 template <typename T>
770 bool Designator<T>::operator==(const Designator<T> &that) const {
771   return TestVariableEquality(*this, that);
772 }
773 bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
774   return field_ == that.field_ && base_ == that.base_ &&
775       dimension_ == that.dimension_;
776 }
777 
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
783 
784 template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;
785