650 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			650 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- 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>
 | |
| 
 | |
| 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_);
 | |
|   DumpExpr(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{detailsIf<DerivedTypeDetails>()};
 | |
|               return derived && derived->isForwardReferenced();
 | |
|             },
 | |
|             [&](const UseDetails &x) {
 | |
|               const auto *use{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_);
 | |
| }
 | |
| 
 | |
| 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();
 | |
|   }
 | |
|   DumpExpr(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());
 | |
|   }
 | |
|   DumpExpr(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) {
 | |
|             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;
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| }
 | |
| 
 | |
| } // namespace Fortran::semantics
 |