| //===-- lib/Semantics/symbol.cpp ------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Semantics/symbol.h" |
| #include "flang/Common/idioms.h" |
| #include "flang/Evaluate/expression.h" |
| #include "flang/Semantics/scope.h" |
| #include "flang/Semantics/semantics.h" |
| #include "flang/Semantics/tools.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <string> |
| #include <type_traits> |
| |
| namespace Fortran::semantics { |
| |
| template <typename T> |
| static void DumpOptional(llvm::raw_ostream &os, const char *label, const T &x) { |
| if (x) { |
| os << ' ' << label << ':' << *x; |
| } |
| } |
| template <typename T> |
| static void DumpExpr(llvm::raw_ostream &os, const char *label, |
| const std::optional<evaluate::Expr<T>> &x) { |
| if (x) { |
| x->AsFortran(os << ' ' << label << ':'); |
| } |
| } |
| |
| static void DumpBool(llvm::raw_ostream &os, const char *label, bool x) { |
| if (x) { |
| os << ' ' << label; |
| } |
| } |
| |
| static void DumpSymbolVector(llvm::raw_ostream &os, const SymbolVector &list) { |
| char sep{' '}; |
| for (const Symbol &elem : list) { |
| os << sep << elem.name(); |
| sep = ','; |
| } |
| } |
| |
| static void DumpType(llvm::raw_ostream &os, const Symbol &symbol) { |
| if (const auto *type{symbol.GetType()}) { |
| os << *type << ' '; |
| } |
| } |
| static void DumpType(llvm::raw_ostream &os, const DeclTypeSpec *type) { |
| if (type) { |
| os << ' ' << *type; |
| } |
| } |
| |
| template <typename T> |
| static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) { |
| if (!list.empty()) { |
| os << ' ' << label << ':'; |
| char sep{' '}; |
| for (const auto &elem : list) { |
| os << sep << elem; |
| sep = ','; |
| } |
| } |
| } |
| |
| const Scope *ModuleDetails::parent() const { |
| return isSubmodule_ && scope_ ? &scope_->parent() : nullptr; |
| } |
| const Scope *ModuleDetails::ancestor() const { |
| return isSubmodule_ && scope_ ? FindModuleContaining(*scope_) : nullptr; |
| } |
| void ModuleDetails::set_scope(const Scope *scope) { |
| CHECK(!scope_); |
| bool scopeIsSubmodule{scope->parent().kind() == Scope::Kind::Module}; |
| CHECK(isSubmodule_ == scopeIsSubmodule); |
| scope_ = scope; |
| } |
| |
| llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &os, const SubprogramDetails &x) { |
| DumpBool(os, "isInterface", x.isInterface_); |
| DumpBool(os, "dummy", x.isDummy_); |
| DumpOptional(os, "bindName", x.bindName()); |
| if (x.result_) { |
| DumpType(os << " result:", x.result()); |
| os << x.result_->name(); |
| if (!x.result_->attrs().empty()) { |
| os << ", " << x.result_->attrs(); |
| } |
| } |
| if (x.entryScope_) { |
| os << " entry"; |
| if (x.entryScope_->symbol()) { |
| os << " in " << x.entryScope_->symbol()->name(); |
| } |
| } |
| char sep{'('}; |
| os << ' '; |
| for (const Symbol *arg : x.dummyArgs_) { |
| os << sep; |
| sep = ','; |
| if (arg) { |
| DumpType(os, *arg); |
| os << arg->name(); |
| } else { |
| os << '*'; |
| } |
| } |
| os << (sep == '(' ? "()" : ")"); |
| if (x.stmtFunction_) { |
| os << " -> " << x.stmtFunction_->AsFortran(); |
| } |
| return os; |
| } |
| |
| void EntityDetails::set_type(const DeclTypeSpec &type) { |
| CHECK(!type_); |
| type_ = &type; |
| } |
| |
| void AssocEntityDetails::set_rank(int rank) { rank_ = rank; } |
| void EntityDetails::ReplaceType(const DeclTypeSpec &type) { type_ = &type; } |
| |
| void ObjectEntityDetails::set_shape(const ArraySpec &shape) { |
| CHECK(shape_.empty()); |
| for (const auto &shapeSpec : shape) { |
| shape_.push_back(shapeSpec); |
| } |
| } |
| void ObjectEntityDetails::set_coshape(const ArraySpec &coshape) { |
| CHECK(coshape_.empty()); |
| for (const auto &shapeSpec : coshape) { |
| coshape_.push_back(shapeSpec); |
| } |
| } |
| |
| ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) { |
| if (type()) { |
| interface_.set_type(*type()); |
| } |
| } |
| |
| UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) { |
| add_occurrence(useDetails.location(), *GetUsedModule(useDetails).scope()); |
| } |
| UseErrorDetails &UseErrorDetails::add_occurrence( |
| const SourceName &location, const Scope &module) { |
| occurrences_.push_back(std::make_pair(location, &module)); |
| return *this; |
| } |
| |
| void GenericDetails::AddSpecificProc( |
| const Symbol &proc, SourceName bindingName) { |
| specificProcs_.push_back(proc); |
| bindingNames_.push_back(bindingName); |
| } |
| void GenericDetails::set_specific(Symbol &specific) { |
| CHECK(!specific_); |
| CHECK(!derivedType_); |
| specific_ = &specific; |
| } |
| void GenericDetails::set_derivedType(Symbol &derivedType) { |
| CHECK(!specific_); |
| CHECK(!derivedType_); |
| derivedType_ = &derivedType; |
| } |
| void GenericDetails::AddUse(const Symbol &use) { |
| CHECK(use.has<UseDetails>()); |
| uses_.push_back(use); |
| } |
| |
| const Symbol *GenericDetails::CheckSpecific() const { |
| return const_cast<GenericDetails *>(this)->CheckSpecific(); |
| } |
| Symbol *GenericDetails::CheckSpecific() { |
| if (specific_) { |
| for (const Symbol &proc : specificProcs_) { |
| if (&proc == specific_) { |
| return nullptr; |
| } |
| } |
| return specific_; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void GenericDetails::CopyFrom(const GenericDetails &from) { |
| CHECK(specificProcs_.size() == bindingNames_.size()); |
| CHECK(from.specificProcs_.size() == from.bindingNames_.size()); |
| kind_ = from.kind_; |
| if (from.derivedType_) { |
| CHECK(!derivedType_ || derivedType_ == from.derivedType_); |
| derivedType_ = from.derivedType_; |
| } |
| for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) { |
| if (std::find_if(specificProcs_.begin(), specificProcs_.end(), |
| [&](const Symbol &mySymbol) { |
| return &mySymbol == &*from.specificProcs_[i]; |
| }) == specificProcs_.end()) { |
| specificProcs_.push_back(from.specificProcs_[i]); |
| bindingNames_.push_back(from.bindingNames_[i]); |
| } |
| } |
| } |
| |
| // The name of the kind of details for this symbol. |
| // This is primarily for debugging. |
| std::string DetailsToString(const Details &details) { |
| return std::visit( |
| common::visitors{ |
| [](const UnknownDetails &) { return "Unknown"; }, |
| [](const MainProgramDetails &) { return "MainProgram"; }, |
| [](const ModuleDetails &) { return "Module"; }, |
| [](const SubprogramDetails &) { return "Subprogram"; }, |
| [](const SubprogramNameDetails &) { return "SubprogramName"; }, |
| [](const EntityDetails &) { return "Entity"; }, |
| [](const ObjectEntityDetails &) { return "ObjectEntity"; }, |
| [](const ProcEntityDetails &) { return "ProcEntity"; }, |
| [](const DerivedTypeDetails &) { return "DerivedType"; }, |
| [](const UseDetails &) { return "Use"; }, |
| [](const UseErrorDetails &) { return "UseError"; }, |
| [](const HostAssocDetails &) { return "HostAssoc"; }, |
| [](const GenericDetails &) { return "Generic"; }, |
| [](const ProcBindingDetails &) { return "ProcBinding"; }, |
| [](const NamelistDetails &) { return "Namelist"; }, |
| [](const CommonBlockDetails &) { return "CommonBlockDetails"; }, |
| [](const TypeParamDetails &) { return "TypeParam"; }, |
| [](const MiscDetails &) { return "Misc"; }, |
| [](const AssocEntityDetails &) { return "AssocEntity"; }, |
| }, |
| details); |
| } |
| |
| const std::string Symbol::GetDetailsName() const { |
| return DetailsToString(details_); |
| } |
| |
| void Symbol::set_details(Details &&details) { |
| CHECK(CanReplaceDetails(details)); |
| details_ = std::move(details); |
| } |
| |
| bool Symbol::CanReplaceDetails(const Details &details) const { |
| if (has<UnknownDetails>()) { |
| return true; // can always replace UnknownDetails |
| } else { |
| return std::visit( |
| common::visitors{ |
| [](const UseErrorDetails &) { return true; }, |
| [&](const ObjectEntityDetails &) { return has<EntityDetails>(); }, |
| [&](const ProcEntityDetails &) { return has<EntityDetails>(); }, |
| [&](const SubprogramDetails &) { |
| return has<SubprogramNameDetails>() || has<EntityDetails>(); |
| }, |
| [&](const DerivedTypeDetails &) { |
| const auto *derived{this->detailsIf<DerivedTypeDetails>()}; |
| return derived && derived->isForwardReferenced(); |
| }, |
| [&](const UseDetails &x) { |
| const auto *use{this->detailsIf<UseDetails>()}; |
| return use && use->symbol() == x.symbol(); |
| }, |
| [](const auto &) { return false; }, |
| }, |
| details); |
| } |
| } |
| |
| // Usually a symbol's name is the first occurrence in the source, but sometimes |
| // we want to replace it with one at a different location (but same characters). |
| void Symbol::ReplaceName(const SourceName &name) { |
| CHECK(name == name_); |
| name_ = name; |
| } |
| |
| void Symbol::SetType(const DeclTypeSpec &type) { |
| std::visit(common::visitors{ |
| [&](EntityDetails &x) { x.set_type(type); }, |
| [&](ObjectEntityDetails &x) { x.set_type(type); }, |
| [&](AssocEntityDetails &x) { x.set_type(type); }, |
| [&](ProcEntityDetails &x) { x.interface().set_type(type); }, |
| [&](TypeParamDetails &x) { x.set_type(type); }, |
| [](auto &) {}, |
| }, |
| details_); |
| } |
| |
| template <typename T> |
| constexpr bool HasBindName{std::is_convertible_v<T, const WithBindName *>}; |
| |
| const std::string *Symbol::GetBindName() const { |
| return std::visit( |
| [&](auto &x) -> const std::string * { |
| if constexpr (HasBindName<decltype(&x)>) { |
| return x.bindName(); |
| } else { |
| return nullptr; |
| } |
| }, |
| details_); |
| } |
| |
| void Symbol::SetBindName(std::string &&name) { |
| std::visit( |
| [&](auto &x) { |
| if constexpr (HasBindName<decltype(&x)>) { |
| x.set_bindName(std::move(name)); |
| } else { |
| DIE("bind name not allowed on this kind of symbol"); |
| } |
| }, |
| details_); |
| } |
| |
| bool Symbol::IsFuncResult() const { |
| return std::visit( |
| common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); }, |
| [](const ObjectEntityDetails &x) { return x.isFuncResult(); }, |
| [](const ProcEntityDetails &x) { return x.isFuncResult(); }, |
| [](const HostAssocDetails &x) { return x.symbol().IsFuncResult(); }, |
| [](const auto &) { return false; }}, |
| details_); |
| } |
| |
| bool Symbol::IsObjectArray() const { |
| const auto *details{std::get_if<ObjectEntityDetails>(&details_)}; |
| return details && details->IsArray(); |
| } |
| |
| bool Symbol::IsSubprogram() const { |
| return std::visit( |
| common::visitors{ |
| [](const SubprogramDetails &) { return true; }, |
| [](const SubprogramNameDetails &) { return true; }, |
| [](const GenericDetails &) { return true; }, |
| [](const UseDetails &x) { return x.symbol().IsSubprogram(); }, |
| [](const auto &) { return false; }, |
| }, |
| details_); |
| } |
| |
| bool Symbol::IsFromModFile() const { |
| return test(Flag::ModFile) || |
| (!owner_->IsGlobal() && owner_->symbol()->IsFromModFile()); |
| } |
| |
| ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d) |
| : EntityDetails(d) {} |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) { |
| DumpBool(os, "dummy", x.isDummy()); |
| DumpBool(os, "funcResult", x.isFuncResult()); |
| if (x.type()) { |
| os << " type: " << *x.type(); |
| } |
| DumpOptional(os, "bindName", x.bindName()); |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &os, const ObjectEntityDetails &x) { |
| os << *static_cast<const EntityDetails *>(&x); |
| DumpList(os, "shape", x.shape()); |
| DumpList(os, "coshape", x.coshape()); |
| DumpExpr(os, "init", x.init_); |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &os, const AssocEntityDetails &x) { |
| os << *static_cast<const EntityDetails *>(&x); |
| if (auto assocRank{x.rank()}) { |
| os << " rank: " << *assocRank; |
| } |
| DumpExpr(os, "expr", x.expr()); |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &os, const ProcEntityDetails &x) { |
| if (auto *symbol{x.interface_.symbol()}) { |
| os << ' ' << symbol->name(); |
| } else { |
| DumpType(os, x.interface_.type()); |
| } |
| DumpOptional(os, "bindName", x.bindName()); |
| DumpOptional(os, "passName", x.passName()); |
| if (x.init()) { |
| if (const Symbol * target{*x.init()}) { |
| os << " => " << target->name(); |
| } else { |
| os << " => NULL()"; |
| } |
| } |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &os, const DerivedTypeDetails &x) { |
| DumpBool(os, "sequence", x.sequence_); |
| DumpList(os, "components", x.componentNames_); |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const GenericDetails &x) { |
| os << ' ' << x.kind().ToString(); |
| DumpBool(os, "(specific)", x.specific() != nullptr); |
| DumpBool(os, "(derivedType)", x.derivedType() != nullptr); |
| if (const auto &uses{x.uses()}; !uses.empty()) { |
| os << " (uses:"; |
| char sep{' '}; |
| for (const Symbol &use : uses) { |
| const Symbol &ultimate{use.GetUltimate()}; |
| os << sep << ultimate.name() << "->" |
| << ultimate.owner().GetName().value(); |
| sep = ','; |
| } |
| os << ')'; |
| } |
| os << " procs:"; |
| DumpSymbolVector(os, x.specificProcs()); |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { |
| os << DetailsToString(details); |
| std::visit( // |
| common::visitors{ |
| [&](const UnknownDetails &) {}, |
| [&](const MainProgramDetails &) {}, |
| [&](const ModuleDetails &x) { |
| if (x.isSubmodule()) { |
| os << " ("; |
| if (x.ancestor()) { |
| auto ancestor{x.ancestor()->GetName().value()}; |
| os << ancestor; |
| if (x.parent()) { |
| auto parent{x.parent()->GetName().value()}; |
| if (ancestor != parent) { |
| os << ':' << parent; |
| } |
| } |
| } |
| os << ")"; |
| } |
| }, |
| [&](const SubprogramNameDetails &x) { |
| os << ' ' << EnumToString(x.kind()); |
| }, |
| [&](const UseDetails &x) { |
| os << " from " << x.symbol().name() << " in " |
| << GetUsedModule(x).name(); |
| }, |
| [&](const UseErrorDetails &x) { |
| os << " uses:"; |
| char sep{':'}; |
| for (const auto &[location, module] : x.occurrences()) { |
| os << sep << " from " << module->GetName().value() << " at " |
| << location; |
| sep = ','; |
| } |
| }, |
| [](const HostAssocDetails &) {}, |
| [&](const ProcBindingDetails &x) { |
| os << " => " << x.symbol().name(); |
| DumpOptional(os, "passName", x.passName()); |
| }, |
| [&](const NamelistDetails &x) { |
| os << ':'; |
| DumpSymbolVector(os, x.objects()); |
| }, |
| [&](const CommonBlockDetails &x) { |
| DumpOptional(os, "bindName", x.bindName()); |
| if (x.alignment()) { |
| os << " alignment=" << x.alignment(); |
| } |
| os << ':'; |
| for (const auto &object : x.objects()) { |
| os << ' ' << object->name(); |
| } |
| }, |
| [&](const TypeParamDetails &x) { |
| DumpOptional(os, "type", x.type()); |
| os << ' ' << common::EnumToString(x.attr()); |
| DumpExpr(os, "init", x.init()); |
| }, |
| [&](const MiscDetails &x) { |
| os << ' ' << MiscDetails::EnumToString(x.kind()); |
| }, |
| [&](const auto &x) { os << x; }, |
| }, |
| details); |
| return os; |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &o, Symbol::Flag flag) { |
| return o << Symbol::EnumToString(flag); |
| } |
| |
| llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &o, const Symbol::Flags &flags) { |
| std::size_t n{flags.count()}; |
| std::size_t seen{0}; |
| for (std::size_t j{0}; seen < n; ++j) { |
| Symbol::Flag flag{static_cast<Symbol::Flag>(j)}; |
| if (flags.test(flag)) { |
| if (seen++ > 0) { |
| o << ", "; |
| } |
| o << flag; |
| } |
| } |
| return o; |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Symbol &symbol) { |
| os << symbol.name(); |
| if (!symbol.attrs().empty()) { |
| os << ", " << symbol.attrs(); |
| } |
| if (!symbol.flags().empty()) { |
| os << " (" << symbol.flags() << ')'; |
| } |
| if (symbol.size_) { |
| os << " size=" << symbol.size_ << " offset=" << symbol.offset_; |
| } |
| os << ": " << symbol.details_; |
| return os; |
| } |
| |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| void Symbol::dump() const { llvm::errs() << *this << '\n'; } |
| #endif |
| |
| // Output a unique name for a scope by qualifying it with the names of |
| // parent scopes. For scopes without corresponding symbols, use the kind |
| // with an index (e.g. Block1, Block2, etc.). |
| static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) { |
| if (!scope.IsGlobal()) { |
| DumpUniqueName(os, scope.parent()); |
| os << '/'; |
| if (auto *scopeSymbol{scope.symbol()}; |
| scopeSymbol && !scopeSymbol->name().empty()) { |
| os << scopeSymbol->name(); |
| } else { |
| int index{1}; |
| for (auto &child : scope.parent().children()) { |
| if (child == scope) { |
| break; |
| } |
| if (child.kind() == scope.kind()) { |
| ++index; |
| } |
| } |
| os << Scope::EnumToString(scope.kind()) << index; |
| } |
| } |
| } |
| |
| // Dump a symbol for UnparseWithSymbols. This will be used for tests so the |
| // format should be reasonably stable. |
| llvm::raw_ostream &DumpForUnparse( |
| llvm::raw_ostream &os, const Symbol &symbol, bool isDef) { |
| DumpUniqueName(os, symbol.owner()); |
| os << '/' << symbol.name(); |
| if (isDef) { |
| if (!symbol.attrs().empty()) { |
| os << ' ' << symbol.attrs(); |
| } |
| if (!symbol.flags().empty()) { |
| os << " (" << symbol.flags() << ')'; |
| } |
| os << ' ' << symbol.GetDetailsName(); |
| DumpType(os, symbol.GetType()); |
| } |
| return os; |
| } |
| |
| const DerivedTypeSpec *Symbol::GetParentTypeSpec(const Scope *scope) const { |
| if (const Symbol * parentComponent{GetParentComponent(scope)}) { |
| const auto &object{parentComponent->get<ObjectEntityDetails>()}; |
| return &object.type()->derivedTypeSpec(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| const Symbol *Symbol::GetParentComponent(const Scope *scope) const { |
| if (const auto *dtDetails{detailsIf<DerivedTypeDetails>()}) { |
| if (const Scope * localScope{scope ? scope : scope_}) { |
| return dtDetails->GetParentComponent(DEREF(localScope)); |
| } |
| } |
| return nullptr; |
| } |
| |
| void DerivedTypeDetails::add_component(const Symbol &symbol) { |
| if (symbol.test(Symbol::Flag::ParentComp)) { |
| CHECK(componentNames_.empty()); |
| } |
| componentNames_.push_back(symbol.name()); |
| } |
| |
| const Symbol *DerivedTypeDetails::GetParentComponent(const Scope &scope) const { |
| if (auto extends{GetParentComponentName()}) { |
| if (auto iter{scope.find(*extends)}; iter != scope.cend()) { |
| if (const Symbol & symbol{*iter->second}; |
| symbol.test(Symbol::Flag::ParentComp)) { |
| return &symbol; |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| const Symbol *DerivedTypeDetails::GetFinalForRank(int rank) const { |
| for (const auto &pair : finals_) { |
| const Symbol &symbol{*pair.second}; |
| if (const auto *details{symbol.detailsIf<SubprogramDetails>()}) { |
| if (details->dummyArgs().size() == 1) { |
| if (const Symbol * arg{details->dummyArgs().at(0)}) { |
| if (const auto *object{arg->detailsIf<ObjectEntityDetails>()}) { |
| if (rank == object->shape().Rank() || object->IsAssumedRank() || |
| symbol.attrs().test(Attr::ELEMENTAL)) { |
| return &symbol; |
| } |
| } |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| void TypeParamDetails::set_type(const DeclTypeSpec &type) { |
| CHECK(!type_); |
| type_ = &type; |
| } |
| |
| bool GenericKind::IsIntrinsicOperator() const { |
| return Is(OtherKind::Concat) || Has<common::LogicalOperator>() || |
| Has<common::NumericOperator>() || Has<common::RelationalOperator>(); |
| } |
| |
| bool GenericKind::IsOperator() const { |
| return IsDefinedOperator() || IsIntrinsicOperator(); |
| } |
| |
| std::string GenericKind::ToString() const { |
| return std::visit( |
| common::visitors { |
| [](const OtherKind &x) { return EnumToString(x); }, |
| [](const DefinedIo &x) { return EnumToString(x); }, |
| #if !__clang__ && __GNUC__ == 7 && __GNUC_MINOR__ == 2 |
| [](const common::NumericOperator &x) { |
| return common::EnumToString(x); |
| }, |
| [](const common::LogicalOperator &x) { |
| return common::EnumToString(x); |
| }, |
| [](const common::RelationalOperator &x) { |
| return common::EnumToString(x); |
| }, |
| #else |
| [](const auto &x) { return common::EnumToString(x); }, |
| #endif |
| }, |
| u); |
| } |
| |
| bool GenericKind::Is(GenericKind::OtherKind x) const { |
| const OtherKind *y{std::get_if<OtherKind>(&u)}; |
| return y && *y == x; |
| } |
| |
| bool SymbolOffsetCompare::operator()( |
| const SymbolRef &x, const SymbolRef &y) const { |
| const Symbol *xCommon{FindCommonBlockContaining(*x)}; |
| const Symbol *yCommon{FindCommonBlockContaining(*y)}; |
| if (xCommon) { |
| if (yCommon) { |
| const SymbolSourcePositionCompare sourceCmp; |
| if (sourceCmp(*xCommon, *yCommon)) { |
| return true; |
| } else if (sourceCmp(*yCommon, *xCommon)) { |
| return false; |
| } else if (x->offset() == y->offset()) { |
| return x->size() > y->size(); |
| } else { |
| return x->offset() < y->offset(); |
| } |
| } else { |
| return false; |
| } |
| } else if (yCommon) { |
| return true; |
| } else if (x->offset() == y->offset()) { |
| return x->size() > y->size(); |
| } else { |
| return x->offset() < y->offset(); |
| } |
| return x->GetSemanticsContext().allCookedSources().Precedes( |
| x->name(), y->name()); |
| } |
| bool SymbolOffsetCompare::operator()( |
| const MutableSymbolRef &x, const MutableSymbolRef &y) const { |
| return (*this)(SymbolRef{*x}, SymbolRef{*y}); |
| } |
| |
| } // namespace Fortran::semantics |