1 //===-- lib/Semantics/symbol.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/Semantics/symbol.h" 10 #include "flang/Common/idioms.h" 11 #include "flang/Evaluate/expression.h" 12 #include "flang/Semantics/scope.h" 13 #include "flang/Semantics/semantics.h" 14 #include "flang/Semantics/tools.h" 15 #include "llvm/Support/raw_ostream.h" 16 #include <string> 17 #include <type_traits> 18 19 namespace Fortran::semantics { 20 21 template <typename T> 22 static void DumpOptional(llvm::raw_ostream &os, const char *label, const T &x) { 23 if (x) { 24 os << ' ' << label << ':' << *x; 25 } 26 } 27 template <typename T> 28 static void DumpExpr(llvm::raw_ostream &os, const char *label, 29 const std::optional<evaluate::Expr<T>> &x) { 30 if (x) { 31 x->AsFortran(os << ' ' << label << ':'); 32 } 33 } 34 35 static void DumpBool(llvm::raw_ostream &os, const char *label, bool x) { 36 if (x) { 37 os << ' ' << label; 38 } 39 } 40 41 static void DumpSymbolVector(llvm::raw_ostream &os, const SymbolVector &list) { 42 char sep{' '}; 43 for (const Symbol &elem : list) { 44 os << sep << elem.name(); 45 sep = ','; 46 } 47 } 48 49 static void DumpType(llvm::raw_ostream &os, const Symbol &symbol) { 50 if (const auto *type{symbol.GetType()}) { 51 os << *type << ' '; 52 } 53 } 54 static void DumpType(llvm::raw_ostream &os, const DeclTypeSpec *type) { 55 if (type) { 56 os << ' ' << *type; 57 } 58 } 59 60 template <typename T> 61 static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) { 62 if (!list.empty()) { 63 os << ' ' << label << ':'; 64 char sep{' '}; 65 for (const auto &elem : list) { 66 os << sep << elem; 67 sep = ','; 68 } 69 } 70 } 71 72 const Scope *ModuleDetails::parent() const { 73 return isSubmodule_ && scope_ ? &scope_->parent() : nullptr; 74 } 75 const Scope *ModuleDetails::ancestor() const { 76 return isSubmodule_ && scope_ ? FindModuleContaining(*scope_) : nullptr; 77 } 78 void ModuleDetails::set_scope(const Scope *scope) { 79 CHECK(!scope_); 80 bool scopeIsSubmodule{scope->parent().kind() == Scope::Kind::Module}; 81 CHECK(isSubmodule_ == scopeIsSubmodule); 82 scope_ = scope; 83 } 84 85 llvm::raw_ostream &operator<<( 86 llvm::raw_ostream &os, const SubprogramDetails &x) { 87 DumpBool(os, "isInterface", x.isInterface_); 88 DumpBool(os, "dummy", x.isDummy_); 89 DumpOptional(os, "bindName", x.bindName()); 90 if (x.result_) { 91 DumpType(os << " result:", x.result()); 92 os << x.result_->name(); 93 if (!x.result_->attrs().empty()) { 94 os << ", " << x.result_->attrs(); 95 } 96 } 97 if (x.entryScope_) { 98 os << " entry"; 99 if (x.entryScope_->symbol()) { 100 os << " in " << x.entryScope_->symbol()->name(); 101 } 102 } 103 char sep{'('}; 104 os << ' '; 105 for (const Symbol *arg : x.dummyArgs_) { 106 os << sep; 107 sep = ','; 108 if (arg) { 109 DumpType(os, *arg); 110 os << arg->name(); 111 } else { 112 os << '*'; 113 } 114 } 115 os << (sep == '(' ? "()" : ")"); 116 if (x.stmtFunction_) { 117 os << " -> " << x.stmtFunction_->AsFortran(); 118 } 119 return os; 120 } 121 122 void EntityDetails::set_type(const DeclTypeSpec &type) { 123 CHECK(!type_); 124 type_ = &type; 125 } 126 127 void AssocEntityDetails::set_rank(int rank) { rank_ = rank; } 128 void EntityDetails::ReplaceType(const DeclTypeSpec &type) { type_ = &type; } 129 130 void ObjectEntityDetails::set_shape(const ArraySpec &shape) { 131 CHECK(shape_.empty()); 132 for (const auto &shapeSpec : shape) { 133 shape_.push_back(shapeSpec); 134 } 135 } 136 void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) { 137 CHECK(coshape_.empty()); 138 for (const auto &shapeSpec : coshape) { 139 coshape_.push_back(shapeSpec); 140 } 141 } 142 143 ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) { 144 if (type()) { 145 interface_.set_type(*type()); 146 } 147 } 148 149 UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) { 150 add_occurrence(useDetails.location(), *GetUsedModule(useDetails).scope()); 151 } 152 UseErrorDetails &UseErrorDetails::add_occurrence( 153 const SourceName &location, const Scope &module) { 154 occurrences_.push_back(std::make_pair(location, &module)); 155 return *this; 156 } 157 158 void GenericDetails::AddSpecificProc( 159 const Symbol &proc, SourceName bindingName) { 160 specificProcs_.push_back(proc); 161 bindingNames_.push_back(bindingName); 162 } 163 void GenericDetails::set_specific(Symbol &specific) { 164 CHECK(!specific_); 165 CHECK(!derivedType_); 166 specific_ = &specific; 167 } 168 void GenericDetails::set_derivedType(Symbol &derivedType) { 169 CHECK(!specific_); 170 CHECK(!derivedType_); 171 derivedType_ = &derivedType; 172 } 173 void GenericDetails::AddUse(const Symbol &use) { 174 CHECK(use.has<UseDetails>()); 175 uses_.push_back(use); 176 } 177 178 const Symbol *GenericDetails::CheckSpecific() const { 179 return const_cast<GenericDetails *>(this)->CheckSpecific(); 180 } 181 Symbol *GenericDetails::CheckSpecific() { 182 if (specific_) { 183 for (const Symbol &proc : specificProcs_) { 184 if (&proc == specific_) { 185 return nullptr; 186 } 187 } 188 return specific_; 189 } else { 190 return nullptr; 191 } 192 } 193 194 void GenericDetails::CopyFrom(const GenericDetails &from) { 195 CHECK(specificProcs_.size() == bindingNames_.size()); 196 CHECK(from.specificProcs_.size() == from.bindingNames_.size()); 197 kind_ = from.kind_; 198 if (from.derivedType_) { 199 CHECK(!derivedType_ || derivedType_ == from.derivedType_); 200 derivedType_ = from.derivedType_; 201 } 202 for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) { 203 if (std::find_if(specificProcs_.begin(), specificProcs_.end(), 204 [&](const Symbol &mySymbol) { 205 return &mySymbol == &*from.specificProcs_[i]; 206 }) == specificProcs_.end()) { 207 specificProcs_.push_back(from.specificProcs_[i]); 208 bindingNames_.push_back(from.bindingNames_[i]); 209 } 210 } 211 } 212 213 // The name of the kind of details for this symbol. 214 // This is primarily for debugging. 215 std::string DetailsToString(const Details &details) { 216 return std::visit( 217 common::visitors{ 218 [](const UnknownDetails &) { return "Unknown"; }, 219 [](const MainProgramDetails &) { return "MainProgram"; }, 220 [](const ModuleDetails &) { return "Module"; }, 221 [](const SubprogramDetails &) { return "Subprogram"; }, 222 [](const SubprogramNameDetails &) { return "SubprogramName"; }, 223 [](const EntityDetails &) { return "Entity"; }, 224 [](const ObjectEntityDetails &) { return "ObjectEntity"; }, 225 [](const ProcEntityDetails &) { return "ProcEntity"; }, 226 [](const DerivedTypeDetails &) { return "DerivedType"; }, 227 [](const UseDetails &) { return "Use"; }, 228 [](const UseErrorDetails &) { return "UseError"; }, 229 [](const HostAssocDetails &) { return "HostAssoc"; }, 230 [](const GenericDetails &) { return "Generic"; }, 231 [](const ProcBindingDetails &) { return "ProcBinding"; }, 232 [](const NamelistDetails &) { return "Namelist"; }, 233 [](const CommonBlockDetails &) { return "CommonBlockDetails"; }, 234 [](const TypeParamDetails &) { return "TypeParam"; }, 235 [](const MiscDetails &) { return "Misc"; }, 236 [](const AssocEntityDetails &) { return "AssocEntity"; }, 237 }, 238 details); 239 } 240 241 const std::string Symbol::GetDetailsName() const { 242 return DetailsToString(details_); 243 } 244 245 void Symbol::set_details(Details &&details) { 246 CHECK(CanReplaceDetails(details)); 247 details_ = std::move(details); 248 } 249 250 bool Symbol::CanReplaceDetails(const Details &details) const { 251 if (has<UnknownDetails>()) { 252 return true; // can always replace UnknownDetails 253 } else { 254 return std::visit( 255 common::visitors{ 256 [](const UseErrorDetails &) { return true; }, 257 [&](const ObjectEntityDetails &) { return has<EntityDetails>(); }, 258 [&](const ProcEntityDetails &) { return has<EntityDetails>(); }, 259 [&](const SubprogramDetails &) { 260 return has<SubprogramNameDetails>() || has<EntityDetails>(); 261 }, 262 [&](const DerivedTypeDetails &) { 263 const auto *derived{this->detailsIf<DerivedTypeDetails>()}; 264 return derived && derived->isForwardReferenced(); 265 }, 266 [&](const UseDetails &x) { 267 const auto *use{this->detailsIf<UseDetails>()}; 268 return use && use->symbol() == x.symbol(); 269 }, 270 [](const auto &) { return false; }, 271 }, 272 details); 273 } 274 } 275 276 // Usually a symbol's name is the first occurrence in the source, but sometimes 277 // we want to replace it with one at a different location (but same characters). 278 void Symbol::ReplaceName(const SourceName &name) { 279 CHECK(name == name_); 280 name_ = name; 281 } 282 283 void Symbol::SetType(const DeclTypeSpec &type) { 284 std::visit(common::visitors{ 285 [&](EntityDetails &x) { x.set_type(type); }, 286 [&](ObjectEntityDetails &x) { x.set_type(type); }, 287 [&](AssocEntityDetails &x) { x.set_type(type); }, 288 [&](ProcEntityDetails &x) { x.interface().set_type(type); }, 289 [&](TypeParamDetails &x) { x.set_type(type); }, 290 [](auto &) {}, 291 }, 292 details_); 293 } 294 295 template <typename T> 296 constexpr bool HasBindName{std::is_convertible_v<T, const WithBindName *>}; 297 298 const std::string *Symbol::GetBindName() const { 299 return std::visit( 300 [&](auto &x) -> const std::string * { 301 if constexpr (HasBindName<decltype(&x)>) { 302 return x.bindName(); 303 } else { 304 return nullptr; 305 } 306 }, 307 details_); 308 } 309 310 void Symbol::SetBindName(std::string &&name) { 311 std::visit( 312 [&](auto &x) { 313 if constexpr (HasBindName<decltype(&x)>) { 314 x.set_bindName(std::move(name)); 315 } else { 316 DIE("bind name not allowed on this kind of symbol"); 317 } 318 }, 319 details_); 320 } 321 322 bool Symbol::IsFuncResult() const { 323 return std::visit( 324 common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); }, 325 [](const ObjectEntityDetails &x) { return x.isFuncResult(); }, 326 [](const ProcEntityDetails &x) { return x.isFuncResult(); }, 327 [](const HostAssocDetails &x) { return x.symbol().IsFuncResult(); }, 328 [](const auto &) { return false; }}, 329 details_); 330 } 331 332 bool Symbol::IsObjectArray() const { 333 const auto *details{std::get_if<ObjectEntityDetails>(&details_)}; 334 return details && details->IsArray(); 335 } 336 337 bool Symbol::IsSubprogram() const { 338 return std::visit( 339 common::visitors{ 340 [](const SubprogramDetails &) { return true; }, 341 [](const SubprogramNameDetails &) { return true; }, 342 [](const GenericDetails &) { return true; }, 343 [](const UseDetails &x) { return x.symbol().IsSubprogram(); }, 344 [](const auto &) { return false; }, 345 }, 346 details_); 347 } 348 349 bool Symbol::IsFromModFile() const { 350 return test(Flag::ModFile) || 351 (!owner_->IsGlobal() && owner_->symbol()->IsFromModFile()); 352 } 353 354 ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d) 355 : EntityDetails(d) {} 356 357 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) { 358 DumpBool(os, "dummy", x.isDummy()); 359 DumpBool(os, "funcResult", x.isFuncResult()); 360 if (x.type()) { 361 os << " type: " << *x.type(); 362 } 363 DumpOptional(os, "bindName", x.bindName()); 364 return os; 365 } 366 367 llvm::raw_ostream &operator<<( 368 llvm::raw_ostream &os, const ObjectEntityDetails &x) { 369 os << *static_cast<const EntityDetails *>(&x); 370 DumpList(os, "shape", x.shape()); 371 DumpList(os, "coshape", x.coshape()); 372 DumpExpr(os, "init", x.init_); 373 return os; 374 } 375 376 llvm::raw_ostream &operator<<( 377 llvm::raw_ostream &os, const AssocEntityDetails &x) { 378 os << *static_cast<const EntityDetails *>(&x); 379 if (auto assocRank{x.rank()}) { 380 os << " rank: " << *assocRank; 381 } 382 DumpExpr(os, "expr", x.expr()); 383 return os; 384 } 385 386 llvm::raw_ostream &operator<<( 387 llvm::raw_ostream &os, const ProcEntityDetails &x) { 388 if (auto *symbol{x.interface_.symbol()}) { 389 os << ' ' << symbol->name(); 390 } else { 391 DumpType(os, x.interface_.type()); 392 } 393 DumpOptional(os, "bindName", x.bindName()); 394 DumpOptional(os, "passName", x.passName()); 395 if (x.init()) { 396 if (const Symbol * target{*x.init()}) { 397 os << " => " << target->name(); 398 } else { 399 os << " => NULL()"; 400 } 401 } 402 return os; 403 } 404 405 llvm::raw_ostream &operator<<( 406 llvm::raw_ostream &os, const DerivedTypeDetails &x) { 407 DumpBool(os, "sequence", x.sequence_); 408 DumpList(os, "components", x.componentNames_); 409 return os; 410 } 411 412 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const GenericDetails &x) { 413 os << ' ' << x.kind().ToString(); 414 DumpBool(os, "(specific)", x.specific() != nullptr); 415 DumpBool(os, "(derivedType)", x.derivedType() != nullptr); 416 if (const auto &uses{x.uses()}; !uses.empty()) { 417 os << " (uses:"; 418 char sep{' '}; 419 for (const Symbol &use : uses) { 420 const Symbol &ultimate{use.GetUltimate()}; 421 os << sep << ultimate.name() << "->" 422 << ultimate.owner().GetName().value(); 423 sep = ','; 424 } 425 os << ')'; 426 } 427 os << " procs:"; 428 DumpSymbolVector(os, x.specificProcs()); 429 return os; 430 } 431 432 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { 433 os << DetailsToString(details); 434 std::visit( // 435 common::visitors{ 436 [&](const UnknownDetails &) {}, 437 [&](const MainProgramDetails &) {}, 438 [&](const ModuleDetails &x) { 439 if (x.isSubmodule()) { 440 os << " ("; 441 if (x.ancestor()) { 442 auto ancestor{x.ancestor()->GetName().value()}; 443 os << ancestor; 444 if (x.parent()) { 445 auto parent{x.parent()->GetName().value()}; 446 if (ancestor != parent) { 447 os << ':' << parent; 448 } 449 } 450 } 451 os << ")"; 452 } 453 }, 454 [&](const SubprogramNameDetails &x) { 455 os << ' ' << EnumToString(x.kind()); 456 }, 457 [&](const UseDetails &x) { 458 os << " from " << x.symbol().name() << " in " 459 << GetUsedModule(x).name(); 460 }, 461 [&](const UseErrorDetails &x) { 462 os << " uses:"; 463 char sep{':'}; 464 for (const auto &[location, module] : x.occurrences()) { 465 os << sep << " from " << module->GetName().value() << " at " 466 << location; 467 sep = ','; 468 } 469 }, 470 [](const HostAssocDetails &) {}, 471 [&](const ProcBindingDetails &x) { 472 os << " => " << x.symbol().name(); 473 DumpOptional(os, "passName", x.passName()); 474 }, 475 [&](const NamelistDetails &x) { 476 os << ':'; 477 DumpSymbolVector(os, x.objects()); 478 }, 479 [&](const CommonBlockDetails &x) { 480 DumpOptional(os, "bindName", x.bindName()); 481 if (x.alignment()) { 482 os << " alignment=" << x.alignment(); 483 } 484 os << ':'; 485 for (const auto &object : x.objects()) { 486 os << ' ' << object->name(); 487 } 488 }, 489 [&](const TypeParamDetails &x) { 490 DumpOptional(os, "type", x.type()); 491 os << ' ' << common::EnumToString(x.attr()); 492 DumpExpr(os, "init", x.init()); 493 }, 494 [&](const MiscDetails &x) { 495 os << ' ' << MiscDetails::EnumToString(x.kind()); 496 }, 497 [&](const auto &x) { os << x; }, 498 }, 499 details); 500 return os; 501 } 502 503 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Symbol::Flag flag) { 504 return o << Symbol::EnumToString(flag); 505 } 506 507 llvm::raw_ostream &operator<<( 508 llvm::raw_ostream &o, const Symbol::Flags &flags) { 509 std::size_t n{flags.count()}; 510 std::size_t seen{0}; 511 for (std::size_t j{0}; seen < n; ++j) { 512 Symbol::Flag flag{static_cast<Symbol::Flag>(j)}; 513 if (flags.test(flag)) { 514 if (seen++ > 0) { 515 o << ", "; 516 } 517 o << flag; 518 } 519 } 520 return o; 521 } 522 523 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) { 524 os << symbol.name(); 525 if (!symbol.attrs().empty()) { 526 os << ", " << symbol.attrs(); 527 } 528 if (!symbol.flags().empty()) { 529 os << " (" << symbol.flags() << ')'; 530 } 531 if (symbol.size_) { 532 os << " size=" << symbol.size_ << " offset=" << symbol.offset_; 533 } 534 os << ": " << symbol.details_; 535 return os; 536 } 537 538 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 539 void Symbol::dump() const { llvm::errs() << *this << '\n'; } 540 #endif 541 542 // Output a unique name for a scope by qualifying it with the names of 543 // parent scopes. For scopes without corresponding symbols, use the kind 544 // with an index (e.g. Block1, Block2, etc.). 545 static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) { 546 if (!scope.IsGlobal()) { 547 DumpUniqueName(os, scope.parent()); 548 os << '/'; 549 if (auto *scopeSymbol{scope.symbol()}; 550 scopeSymbol && !scopeSymbol->name().empty()) { 551 os << scopeSymbol->name(); 552 } else { 553 int index{1}; 554 for (auto &child : scope.parent().children()) { 555 if (child == scope) { 556 break; 557 } 558 if (child.kind() == scope.kind()) { 559 ++index; 560 } 561 } 562 os << Scope::EnumToString(scope.kind()) << index; 563 } 564 } 565 } 566 567 // Dump a symbol for UnparseWithSymbols. This will be used for tests so the 568 // format should be reasonably stable. 569 llvm::raw_ostream &DumpForUnparse( 570 llvm::raw_ostream &os, const Symbol &symbol, bool isDef) { 571 DumpUniqueName(os, symbol.owner()); 572 os << '/' << symbol.name(); 573 if (isDef) { 574 if (!symbol.attrs().empty()) { 575 os << ' ' << symbol.attrs(); 576 } 577 if (!symbol.flags().empty()) { 578 os << " (" << symbol.flags() << ')'; 579 } 580 os << ' ' << symbol.GetDetailsName(); 581 DumpType(os, symbol.GetType()); 582 } 583 return os; 584 } 585 586 const DerivedTypeSpec *Symbol::GetParentTypeSpec(const Scope *scope) const { 587 if (const Symbol * parentComponent{GetParentComponent(scope)}) { 588 const auto &object{parentComponent->get<ObjectEntityDetails>()}; 589 return &object.type()->derivedTypeSpec(); 590 } else { 591 return nullptr; 592 } 593 } 594 595 const Symbol *Symbol::GetParentComponent(const Scope *scope) const { 596 if (const auto *dtDetails{detailsIf<DerivedTypeDetails>()}) { 597 if (const Scope * localScope{scope ? scope : scope_}) { 598 return dtDetails->GetParentComponent(DEREF(localScope)); 599 } 600 } 601 return nullptr; 602 } 603 604 void DerivedTypeDetails::add_component(const Symbol &symbol) { 605 if (symbol.test(Symbol::Flag::ParentComp)) { 606 CHECK(componentNames_.empty()); 607 } 608 componentNames_.push_back(symbol.name()); 609 } 610 611 const Symbol *DerivedTypeDetails::GetParentComponent(const Scope &scope) const { 612 if (auto extends{GetParentComponentName()}) { 613 if (auto iter{scope.find(*extends)}; iter != scope.cend()) { 614 if (const Symbol & symbol{*iter->second}; 615 symbol.test(Symbol::Flag::ParentComp)) { 616 return &symbol; 617 } 618 } 619 } 620 return nullptr; 621 } 622 623 const Symbol *DerivedTypeDetails::GetFinalForRank(int rank) const { 624 for (const auto &pair : finals_) { 625 const Symbol &symbol{*pair.second}; 626 if (const auto *details{symbol.detailsIf<SubprogramDetails>()}) { 627 if (details->dummyArgs().size() == 1) { 628 if (const Symbol * arg{details->dummyArgs().at(0)}) { 629 if (const auto *object{arg->detailsIf<ObjectEntityDetails>()}) { 630 if (rank == object->shape().Rank() || object->IsAssumedRank() || 631 symbol.attrs().test(Attr::ELEMENTAL)) { 632 return &symbol; 633 } 634 } 635 } 636 } 637 } 638 } 639 return nullptr; 640 } 641 642 void TypeParamDetails::set_type(const DeclTypeSpec &type) { 643 CHECK(!type_); 644 type_ = &type; 645 } 646 647 bool GenericKind::IsIntrinsicOperator() const { 648 return Is(OtherKind::Concat) || Has<common::LogicalOperator>() || 649 Has<common::NumericOperator>() || Has<common::RelationalOperator>(); 650 } 651 652 bool GenericKind::IsOperator() const { 653 return IsDefinedOperator() || IsIntrinsicOperator(); 654 } 655 656 std::string GenericKind::ToString() const { 657 return std::visit( 658 common::visitors { 659 [](const OtherKind &x) { return EnumToString(x); }, 660 [](const DefinedIo &x) { return EnumToString(x); }, 661 #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2 662 [](const common::NumericOperator &x) { 663 return common::EnumToString(x); 664 }, 665 [](const common::LogicalOperator &x) { 666 return common::EnumToString(x); 667 }, 668 [](const common::RelationalOperator &x) { 669 return common::EnumToString(x); 670 }, 671 #else 672 [](const auto &x) { return common::EnumToString(x); }, 673 #endif 674 }, 675 u); 676 } 677 678 bool GenericKind::Is(GenericKind::OtherKind x) const { 679 const OtherKind *y{std::get_if<OtherKind>(&u)}; 680 return y && *y == x; 681 } 682 683 bool SymbolOffsetCompare::operator()( 684 const SymbolRef &x, const SymbolRef &y) const { 685 const Symbol *xCommon{FindCommonBlockContaining(*x)}; 686 const Symbol *yCommon{FindCommonBlockContaining(*y)}; 687 if (xCommon) { 688 if (yCommon) { 689 const SymbolSourcePositionCompare sourceCmp; 690 if (sourceCmp(*xCommon, *yCommon)) { 691 return true; 692 } else if (sourceCmp(*yCommon, *xCommon)) { 693 return false; 694 } else if (x->offset() == y->offset()) { 695 return x->size() > y->size(); 696 } else { 697 return x->offset() < y->offset(); 698 } 699 } else { 700 return false; 701 } 702 } else if (yCommon) { 703 return true; 704 } else if (x->offset() == y->offset()) { 705 return x->size() > y->size(); 706 } else { 707 return x->offset() < y->offset(); 708 } 709 return x->GetSemanticsContext().allCookedSources().Precedes( 710 x->name(), y->name()); 711 } 712 bool SymbolOffsetCompare::operator()( 713 const MutableSymbolRef &x, const MutableSymbolRef &y) const { 714 return (*this)(SymbolRef{*x}, SymbolRef{*y}); 715 } 716 717 } // namespace Fortran::semantics 718