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