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