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 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 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 (!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 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 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 } 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 286 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 298 std::optional<Expr<SubscriptInteger>> Component::LEN() const { 299 return SymbolLEN(GetLastSymbol()); 300 } 301 302 std::optional<Expr<SubscriptInteger>> NamedEntity::LEN() const { 303 return SymbolLEN(GetLastSymbol()); 304 } 305 306 std::optional<Expr<SubscriptInteger>> ArrayRef::LEN() const { 307 return base_.LEN(); 308 } 309 310 std::optional<Expr<SubscriptInteger>> CoarrayRef::LEN() const { 311 return SymbolLEN(GetLastSymbol()); 312 } 313 314 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 322 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> 333 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 346 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() 372 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 380 int Component::Rank() const { 381 if (int rank{symbol_->Rank()}; rank > 0) { 382 return rank; 383 } 384 return base().Rank(); 385 } 386 387 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 395 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 405 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 419 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 431 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 439 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 448 int ComplexPart::Rank() const { return complex_.Rank(); } 449 450 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. 459 const Symbol &Component::GetFirstSymbol() const { 460 return base_.value().GetFirstSymbol(); 461 } 462 463 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 473 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 483 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 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 500 const Symbol &ArrayRef::GetFirstSymbol() const { 501 return base_.GetFirstSymbol(); 502 } 503 504 const Symbol &ArrayRef::GetLastSymbol() const { return base_.GetLastSymbol(); } 505 506 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 514 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 522 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 534 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 543 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 561 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> 580 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 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 609 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. 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. 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 641 bool BaseObject::operator==(const BaseObject &that) const { 642 return TestVariableEquality(*this, that); 643 } 644 bool Component::operator==(const Component &that) const { 645 return base_ == that.base_ && &*symbol_ == &*that.symbol_; 646 } 647 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 } 655 bool TypeParamInquiry::operator==(const TypeParamInquiry &that) const { 656 return &*parameter_ == &*that.parameter_ && base_ == that.base_; 657 } 658 bool Triplet::operator==(const Triplet &that) const { 659 return lower_ == that.lower_ && upper_ == that.upper_ && 660 stride_ == that.stride_; 661 } 662 bool Subscript::operator==(const Subscript &that) const { return u == that.u; } 663 bool ArrayRef::operator==(const ArrayRef &that) const { 664 return base_ == that.base_ && subscript_ == that.subscript_; 665 } 666 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 } 671 bool DataRef::operator==(const DataRef &that) const { 672 return TestVariableEquality(*this, that); 673 } 674 bool Substring::operator==(const Substring &that) const { 675 return parent_ == that.parent_ && lower_ == that.lower_ && 676 upper_ == that.upper_; 677 } 678 bool ComplexPart::operator==(const ComplexPart &that) const { 679 return part_ == that.part_ && complex_ == that.complex_; 680 } 681 bool ProcedureRef::operator==(const ProcedureRef &that) const { 682 return proc_ == that.proc_ && arguments_ == that.arguments_; 683 } 684 template <typename T> 685 bool Designator<T>::operator==(const Designator<T> &that) const { 686 return TestVariableEquality(*this, that); 687 } 688 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