//===-- 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 namespace Fortran::semantics { template static void DumpOptional(llvm::raw_ostream &os, const char *label, const T &x) { if (x) { os << ' ' << label << ':' << *x; } } template static void DumpExpr(llvm::raw_ostream &os, const char *label, const std::optional> &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 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()); uses_.push_back(use); } const Symbol *GenericDetails::CheckSpecific() const { return const_cast(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()) { return true; // can always replace UnknownDetails } else { return std::visit( common::visitors{ [](const UseErrorDetails &) { return true; }, [&](const ObjectEntityDetails &) { return has(); }, [&](const ProcEntityDetails &) { return has(); }, [&](const SubprogramDetails &) { return has() || has(); }, [&](const DerivedTypeDetails &) { const auto *derived{detailsIf()}; return derived && derived->isForwardReferenced(); }, [&](const UseDetails &x) { const auto *use{detailsIf()}; 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(&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(&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(&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:"; for (const auto &[location, module] : x.occurrences()) { os << " from " << module->GetName().value() << " at " << location; } }, [](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(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()}; return &object.type()->derivedTypeSpec(); } else { return nullptr; } } const Symbol *Symbol::GetParentComponent(const Scope *scope) const { if (const auto *dtDetails{detailsIf()}) { 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()}) { if (details->dummyArgs().size() == 1) { if (const Symbol * arg{details->dummyArgs().at(0)}) { if (const auto *object{arg->detailsIf()}) { 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() || Has() || Has(); } 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(&u)}; return y && *y == x; } } // namespace Fortran::semantics