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
Triplet()27 Triplet::Triplet() : stride_{Expr<SubscriptInteger>{1}} {}
28
Triplet(std::optional<Expr<SubscriptInteger>> && l,std::optional<Expr<SubscriptInteger>> && u,std::optional<Expr<SubscriptInteger>> && s)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
lower() const41 std::optional<Expr<SubscriptInteger>> Triplet::lower() const {
42 if (lower_) {
43 return {lower_.value().value()};
44 }
45 return std::nullopt;
46 }
47
set_lower(Expr<SubscriptInteger> && expr)48 Triplet &Triplet::set_lower(Expr<SubscriptInteger> &&expr) {
49 lower_.emplace(std::move(expr));
50 return *this;
51 }
52
upper() const53 std::optional<Expr<SubscriptInteger>> Triplet::upper() const {
54 if (upper_) {
55 return {upper_.value().value()};
56 }
57 return std::nullopt;
58 }
59
set_upper(Expr<SubscriptInteger> && expr)60 Triplet &Triplet::set_upper(Expr<SubscriptInteger> &&expr) {
61 upper_.emplace(std::move(expr));
62 return *this;
63 }
64
stride() const65 Expr<SubscriptInteger> Triplet::stride() const { return stride_.value(); }
66
set_stride(Expr<SubscriptInteger> && expr)67 Triplet &Triplet::set_stride(Expr<SubscriptInteger> &&expr) {
68 stride_.value() = std::move(expr);
69 return *this;
70 }
71
IsStrideOne() const72 bool Triplet::IsStrideOne() const {
73 if (auto stride{ToInt64(stride_.value())}) {
74 return stride == 1;
75 } else {
76 return false;
77 }
78 }
79
CoarrayRef(SymbolVector && base,std::vector<Subscript> && ss,std::vector<Expr<SubscriptInteger>> && css)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
stat() const88 std::optional<Expr<SomeInteger>> CoarrayRef::stat() const {
89 if (stat_) {
90 return stat_.value().value();
91 } else {
92 return std::nullopt;
93 }
94 }
95
team() const96 std::optional<Expr<SomeInteger>> CoarrayRef::team() const {
97 if (team_) {
98 return team_.value().value();
99 } else {
100 return std::nullopt;
101 }
102 }
103
set_stat(Expr<SomeInteger> && v)104 CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
105 CHECK(IsVariable(v));
106 stat_.emplace(std::move(v));
107 return *this;
108 }
109
set_team(Expr<SomeInteger> && v,bool isTeamNumber)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
GetFirstSymbol() const117 const Symbol &CoarrayRef::GetFirstSymbol() const { return base_.front(); }
118
GetLastSymbol() const119 const Symbol &CoarrayRef::GetLastSymbol() const { return base_.back(); }
120
SetBounds(std::optional<Expr<SubscriptInteger>> & lower,std::optional<Expr<SubscriptInteger>> & upper)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
lower() const131 Expr<SubscriptInteger> Substring::lower() const {
132 if (lower_) {
133 return lower_.value().value();
134 } else {
135 return AsExpr(Constant<SubscriptInteger>{1});
136 }
137 }
138
set_lower(Expr<SubscriptInteger> && expr)139 Substring &Substring::set_lower(Expr<SubscriptInteger> &&expr) {
140 lower_.emplace(std::move(expr));
141 return *this;
142 }
143
upper() const144 std::optional<Expr<SubscriptInteger>> Substring::upper() const {
145 if (upper_) {
146 return upper_.value().value();
147 } else {
148 return common::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
set_upper(Expr<SubscriptInteger> && expr)160 Substring &Substring::set_upper(Expr<SubscriptInteger> &&expr) {
161 upper_.emplace(std::move(expr));
162 return *this;
163 }
164
Fold(FoldingContext & context)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 = common::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
DescriptorInquiry(const NamedEntity & base,Field field,int dim)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
DescriptorInquiry(NamedEntity && base,Field field,int dim)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()
SymbolLEN(const Symbol & symbol)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 (auto constLen{ToInt64(*len)}) {
271 return Expr<SubscriptInteger>{std::max<std::int64_t>(*constLen, 0)};
272 } else if (ultimate.owner().IsDerivedType() ||
273 IsScopeInvariantExpr(*len)) {
274 return AsExpr(Extremum<SubscriptInteger>{
275 Ordering::Greater, Expr<SubscriptInteger>{0}, std::move(*len)});
276 }
277 }
278 }
279 if (IsDescriptor(ultimate) && !ultimate.owner().IsDerivedType()) {
280 return Expr<SubscriptInteger>{
281 DescriptorInquiry{NamedEntity{symbol}, DescriptorInquiry::Field::Len}};
282 }
283 return std::nullopt;
284 }
285
LEN() const286 std::optional<Expr<SubscriptInteger>> BaseObject::LEN() const {
287 return common::visit(
288 common::visitors{
289 [](const Symbol &symbol) { return SymbolLEN(symbol); },
290 [](const StaticDataObject::Pointer &object)
291 -> std::optional<Expr<SubscriptInteger>> {
292 return AsExpr(Constant<SubscriptInteger>{object->data().size()});
293 },
294 },
295 u);
296 }
297
LEN() const298 std::optional<Expr<SubscriptInteger>> Component::LEN() const {
299 return SymbolLEN(GetLastSymbol());
300 }
301
LEN() const302 std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const {
303 return SymbolLEN(GetLastSymbol());
304 }
305
LEN() const306 std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const {
307 return base_.LEN();
308 }
309
LEN() const310 std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const {
311 return SymbolLEN(GetLastSymbol());
312 }
313
LEN() const314 std::optional<Expr<SubscriptInteger>> DataRef::LEN() const {
315 return common::visit(common::visitors{
316 [](SymbolRef symbol) { return SymbolLEN(symbol); },
317 [](const auto &x) { return x.LEN(); },
318 },
319 u);
320 }
321
LEN() const322 std::optional<Expr<SubscriptInteger>> Substring::LEN() const {
323 if (auto top{upper()}) {
324 return AsExpr(Extremum<SubscriptInteger>{Ordering::Greater,
325 AsExpr(Constant<SubscriptInteger>{0}),
326 *std::move(top) - lower() + AsExpr(Constant<SubscriptInteger>{1})});
327 } else {
328 return std::nullopt;
329 }
330 }
331
332 template <typename T>
LEN() const333 std::optional<Expr<SubscriptInteger>> Designator<T>::LEN() const {
334 if constexpr (T::category == TypeCategory::Character) {
335 return common::visit(common::visitors{
336 [](SymbolRef symbol) { return SymbolLEN(symbol); },
337 [](const auto &x) { return x.LEN(); },
338 },
339 u);
340 } else {
341 common::die("Designator<non-char>::LEN() called");
342 return std::nullopt;
343 }
344 }
345
LEN() const346 std::optional<Expr<SubscriptInteger>> ProcedureDesignator::LEN() const {
347 using T = std::optional<Expr<SubscriptInteger>>;
348 return common::visit(
349 common::visitors{
350 [](SymbolRef symbol) -> T { return SymbolLEN(symbol); },
351 [](const common::CopyableIndirection<Component> &c) -> T {
352 return c.value().LEN();
353 },
354 [](const SpecificIntrinsic &i) -> T {
355 // Some cases whose results' lengths can be determined
356 // from the lengths of their arguments are handled in
357 // ProcedureRef::LEN() before coming here.
358 if (const auto &result{i.characteristics.value().functionResult}) {
359 if (const auto *type{result->GetTypeAndShape()}) {
360 if (auto length{type->type().GetCharLength()}) {
361 return std::move(*length);
362 }
363 }
364 }
365 return std::nullopt;
366 },
367 },
368 u);
369 }
370
371 // Rank()
Rank() const372 int BaseObject::Rank() const {
373 return common::visit(common::visitors{
374 [](SymbolRef symbol) { return symbol->Rank(); },
375 [](const StaticDataObject::Pointer &) { return 0; },
376 },
377 u);
378 }
379
Rank() const380 int Component::Rank() const {
381 if (int rank{symbol_->Rank()}; rank > 0) {
382 return rank;
383 }
384 return base().Rank();
385 }
386
Rank() const387 int NamedEntity::Rank() const {
388 return common::visit(common::visitors{
389 [](const SymbolRef s) { return s->Rank(); },
390 [](const Component &c) { return c.Rank(); },
391 },
392 u_);
393 }
394
Rank() const395 int Subscript::Rank() const {
396 return common::visit(common::visitors{
397 [](const IndirectSubscriptIntegerExpr &x) {
398 return x.value().Rank();
399 },
400 [](const Triplet &) { return 1; },
401 },
402 u);
403 }
404
Rank() const405 int ArrayRef::Rank() const {
406 int rank{0};
407 for (const auto &expr : subscript_) {
408 rank += expr.Rank();
409 }
410 if (rank > 0) {
411 return rank;
412 } else if (const Component * component{base_.UnwrapComponent()}) {
413 return component->base().Rank();
414 } else {
415 return 0;
416 }
417 }
418
Rank() const419 int CoarrayRef::Rank() const {
420 if (!subscript_.empty()) {
421 int rank{0};
422 for (const auto &expr : subscript_) {
423 rank += expr.Rank();
424 }
425 return rank;
426 } else {
427 return base_.back()->Rank();
428 }
429 }
430
Rank() const431 int DataRef::Rank() const {
432 return common::visit(common::visitors{
433 [](SymbolRef symbol) { return symbol->Rank(); },
434 [](const auto &x) { return x.Rank(); },
435 },
436 u);
437 }
438
Rank() const439 int Substring::Rank() const {
440 return common::visit(
441 common::visitors{
442 [](const DataRef &dataRef) { return dataRef.Rank(); },
443 [](const StaticDataObject::Pointer &) { return 0; },
444 },
445 parent_);
446 }
447
Rank() const448 int ComplexPart::Rank() const { return complex_.Rank(); }
449
Rank() const450 template <typename T> int Designator<T>::Rank() const {
451 return common::visit(common::visitors{
452 [](SymbolRef symbol) { return symbol->Rank(); },
453 [](const auto &x) { return x.Rank(); },
454 },
455 u);
456 }
457
458 // GetBaseObject(), GetFirstSymbol(), GetLastSymbol(), &c.
GetFirstSymbol() const459 const Symbol &Component::GetFirstSymbol() const {
460 return base_.value().GetFirstSymbol();
461 }
462
GetFirstSymbol() const463 const Symbol &NamedEntity::GetFirstSymbol() const {
464 return common::visit(common::visitors{
465 [](SymbolRef s) -> const Symbol & { return s; },
466 [](const Component &c) -> const Symbol & {
467 return c.GetFirstSymbol();
468 },
469 },
470 u_);
471 }
472
GetLastSymbol() const473 const Symbol &NamedEntity::GetLastSymbol() const {
474 return common::visit(common::visitors{
475 [](SymbolRef s) -> const Symbol & { return s; },
476 [](const Component &c) -> const Symbol & {
477 return c.GetLastSymbol();
478 },
479 },
480 u_);
481 }
482
UnwrapComponent() const483 const Component *NamedEntity::UnwrapComponent() const {
484 return common::visit(
485 common::visitors{
486 [](SymbolRef) -> const Component * { return nullptr; },
487 [](const Component &c) { return &c; },
488 },
489 u_);
490 }
491
UnwrapComponent()492 Component *NamedEntity::UnwrapComponent() {
493 return common::visit(common::visitors{
494 [](SymbolRef &) -> Component * { return nullptr; },
495 [](Component &c) { return &c; },
496 },
497 u_);
498 }
499
GetFirstSymbol() const500 const Symbol &ArrayRef::GetFirstSymbol() const {
501 return base_.GetFirstSymbol();
502 }
503
GetLastSymbol() const504 const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); }
505
GetFirstSymbol() const506 const Symbol &DataRef::GetFirstSymbol() const {
507 return *common::visit(common::visitors{
508 [](SymbolRef symbol) { return &*symbol; },
509 [](const auto &x) { return &x.GetFirstSymbol(); },
510 },
511 u);
512 }
513
GetLastSymbol() const514 const Symbol &DataRef::GetLastSymbol() const {
515 return *common::visit(common::visitors{
516 [](SymbolRef symbol) { return &*symbol; },
517 [](const auto &x) { return &x.GetLastSymbol(); },
518 },
519 u);
520 }
521
GetBaseObject() const522 BaseObject Substring::GetBaseObject() const {
523 return common::visit(common::visitors{
524 [](const DataRef &dataRef) {
525 return BaseObject{dataRef.GetFirstSymbol()};
526 },
527 [](StaticDataObject::Pointer pointer) {
528 return BaseObject{std::move(pointer)};
529 },
530 },
531 parent_);
532 }
533
GetLastSymbol() const534 const Symbol *Substring::GetLastSymbol() const {
535 return common::visit(
536 common::visitors{
537 [](const DataRef &dataRef) { return &dataRef.GetLastSymbol(); },
538 [](const auto &) -> const Symbol * { return nullptr; },
539 },
540 parent_);
541 }
542
GetBaseObject() const543 template <typename T> BaseObject Designator<T>::GetBaseObject() const {
544 return common::visit(
545 common::visitors{
546 [](SymbolRef symbol) { return BaseObject{symbol}; },
547 [](const Substring &sstring) { return sstring.GetBaseObject(); },
548 [](const auto &x) {
549 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
550 if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
551 Substring>) {
552 return x.GetBaseObject();
553 } else
554 #endif
555 return BaseObject{x.GetFirstSymbol()};
556 },
557 },
558 u);
559 }
560
GetLastSymbol() const561 template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
562 return common::visit(
563 common::visitors{
564 [](SymbolRef symbol) { return &*symbol; },
565 [](const Substring &sstring) { return sstring.GetLastSymbol(); },
566 [](const auto &x) {
567 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2
568 if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
569 Substring>) {
570 return x.GetLastSymbol();
571 } else
572 #endif
573 return &x.GetLastSymbol();
574 },
575 },
576 u);
577 }
578
579 template <typename T>
GetType() const580 std::optional<DynamicType> Designator<T>::GetType() const {
581 if constexpr (IsLengthlessIntrinsicType<Result>) {
582 return Result::GetType();
583 } else if (const Symbol * symbol{GetLastSymbol()}) {
584 return DynamicType::From(*symbol);
585 } else if constexpr (Result::category == TypeCategory::Character) {
586 if (const Substring * substring{std::get_if<Substring>(&u)}) {
587 const auto *parent{substring->GetParentIf<StaticDataObject::Pointer>()};
588 CHECK(parent);
589 return DynamicType{TypeCategory::Character, (*parent)->itemBytes()};
590 }
591 }
592 return std::nullopt;
593 }
594
AsNamedEntity(const SymbolVector & x)595 static NamedEntity AsNamedEntity(const SymbolVector &x) {
596 CHECK(!x.empty());
597 NamedEntity result{x.front()};
598 int j{0};
599 for (const Symbol &symbol : x) {
600 if (j++ != 0) {
601 DataRef base{result.IsSymbol() ? DataRef{result.GetLastSymbol()}
602 : DataRef{result.GetComponent()}};
603 result = NamedEntity{Component{std::move(base), symbol}};
604 }
605 }
606 return result;
607 }
608
GetBase() const609 NamedEntity CoarrayRef::GetBase() const { return AsNamedEntity(base_); }
610
611 // Equality testing
612
613 // For the purposes of comparing type parameter expressions while
614 // testing the compatibility of procedure characteristics, two
615 // object dummy arguments with the same name are considered equal.
AreSameSymbol(const Symbol & x,const Symbol & y)616 static bool AreSameSymbol(const Symbol &x, const Symbol &y) {
617 if (&x == &y) {
618 return true;
619 }
620 if (x.name() == y.name()) {
621 if (const auto *xObject{x.detailsIf<semantics::ObjectEntityDetails>()}) {
622 if (const auto *yObject{y.detailsIf<semantics::ObjectEntityDetails>()}) {
623 return xObject->isDummy() && yObject->isDummy();
624 }
625 }
626 }
627 return false;
628 }
629
630 // Implements operator==() for a union type, using special case handling
631 // for Symbol references.
TestVariableEquality(const A & x,const A & y)632 template <typename A> static bool TestVariableEquality(const A &x, const A &y) {
633 const SymbolRef *xSymbol{std::get_if<SymbolRef>(&x.u)};
634 if (const SymbolRef * ySymbol{std::get_if<SymbolRef>(&y.u)}) {
635 return xSymbol && AreSameSymbol(*xSymbol, *ySymbol);
636 } else {
637 return x.u == y.u;
638 }
639 }
640
operator ==(const BaseObject & that) const641 bool BaseObject::operator==(const BaseObject &that) const {
642 return TestVariableEquality(*this, that);
643 }
operator ==(const Component & that) const644 bool Component::operator==(const Component &that) const {
645 return base_ == that.base_ && &*symbol_ == &*that.symbol_;
646 }
operator ==(const NamedEntity & that) const647 bool NamedEntity::operator==(const NamedEntity &that) const {
648 if (IsSymbol()) {
649 return that.IsSymbol() &&
650 AreSameSymbol(GetFirstSymbol(), that.GetFirstSymbol());
651 } else {
652 return !that.IsSymbol() && GetComponent() == that.GetComponent();
653 }
654 }
operator ==(const TypeParamInquiry & that) const655 bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const {
656 return &*parameter_ == &*that.parameter_ && base_ == that.base_;
657 }
operator ==(const Triplet & that) const658 bool Triplet::operator==(const Triplet &that) const {
659 return lower_ == that.lower_ && upper_ == that.upper_ &&
660 stride_ == that.stride_;
661 }
operator ==(const Subscript & that) const662 bool Subscript::operator==(const Subscript &that) const { return u == that.u; }
operator ==(const ArrayRef & that) const663 bool ArrayRef::operator==(const ArrayRef &that) const {
664 return base_ == that.base_ && subscript_ == that.subscript_;
665 }
operator ==(const CoarrayRef & that) const666 bool CoarrayRef::operator==(const CoarrayRef &that) const {
667 return base_ == that.base_ && subscript_ == that.subscript_ &&
668 cosubscript_ == that.cosubscript_ && stat_ == that.stat_ &&
669 team_ == that.team_ && teamIsTeamNumber_ == that.teamIsTeamNumber_;
670 }
operator ==(const DataRef & that) const671 bool DataRef::operator==(const DataRef &that) const {
672 return TestVariableEquality(*this, that);
673 }
operator ==(const Substring & that) const674 bool Substring::operator==(const Substring &that) const {
675 return parent_ == that.parent_ && lower_ == that.lower_ &&
676 upper_ == that.upper_;
677 }
operator ==(const ComplexPart & that) const678 bool ComplexPart::operator==(const ComplexPart &that) const {
679 return part_ == that.part_ && complex_ == that.complex_;
680 }
operator ==(const ProcedureRef & that) const681 bool ProcedureRef::operator==(const ProcedureRef &that) const {
682 return proc_ == that.proc_ && arguments_ == that.arguments_;
683 }
684 template <typename T>
operator ==(const Designator<T> & that) const685 bool Designator<T>::operator==(const Designator<T> &that) const {
686 return TestVariableEquality(*this, that);
687 }
operator ==(const DescriptorInquiry & that) const688 bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
689 return field_ == that.field_ && base_ == that.base_ &&
690 dimension_ == that.dimension_;
691 }
692
693 #ifdef _MSC_VER // disable bogus warning about missing definitions
694 #pragma warning(disable : 4661)
695 #endif
696 INSTANTIATE_VARIABLE_TEMPLATES
697 } // namespace Fortran::evaluate
698
699 template class Fortran::common::Indirection<Fortran::evaluate::Component, true>;
700