// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "symbol.h" #include "scope.h" #include "semantics.h" #include "../common/idioms.h" #include "../evaluate/fold.h" #include #include namespace Fortran::semantics { std::ostream &operator<<(std::ostream &os, const parser::CharBlock &name) { return os << name.ToString(); } const Scope *ModuleDetails::parent() const { return isSubmodule_ && scope_ ? &scope_->parent() : nullptr; } const Scope *ModuleDetails::ancestor() const { if (!isSubmodule_ || !scope_) { return nullptr; } for (auto *scope{scope_};;) { auto *parent{&scope->parent()}; if (parent->kind() != Scope::Kind::Module) { return scope; } scope = parent; } } void ModuleDetails::set_scope(const Scope *scope) { CHECK(!scope_); bool scopeIsSubmodule{scope->parent().kind() == Scope::Kind::Module}; CHECK(isSubmodule_ == scopeIsSubmodule); scope_ = scope; } std::ostream &operator<<(std::ostream &os, const SubprogramDetails &x) { if (x.isInterface_) { os << " isInterface"; } if (x.bindName_) { x.bindName_->AsFortran(os << " bindName:"); } if (x.result_) { os << " result:" << x.result_.value()->name(); } if (x.dummyArgs_.empty()) { char sep{'('}; os << ' '; for (const auto *arg : x.dummyArgs_) { os << sep << arg->name(); sep = ','; } os << ')'; } return os; } void EntityDetails::set_type(const DeclTypeSpec &type) { CHECK(!type_); type_ = &type; } 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); } } ProcEntityDetails::ProcEntityDetails(EntityDetails &&d) : EntityDetails(d) { if (type()) { interface_.set_type(*type()); } } const Symbol &UseDetails::module() const { // owner is a module so it must have a symbol: return *symbol_->owner().symbol(); } UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) { add_occurrence(useDetails.location(), *useDetails.module().scope()); } UseErrorDetails &UseErrorDetails::add_occurrence( const SourceName &location, const Scope &module) { occurrences_.push_back(std::make_pair(location, &module)); return *this; } GenericDetails::GenericDetails(const SymbolList &specificProcs) { for (const auto *proc : specificProcs) { add_specificProc(*proc); } } void GenericDetails::set_specific(Symbol &specific) { CHECK(!specific_); specific_ = &specific; } void GenericDetails::set_derivedType(Symbol &derivedType) { CHECK(!derivedType_); derivedType_ = &derivedType; } const Symbol *GenericDetails::CheckSpecific() const { if (specific_) { for (const auto *proc : specificProcs_) { if (proc == specific_) { return nullptr; } } return specific_; } else { return nullptr; } } // 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 GenericBindingDetails &) { return "GenericBinding"; }, [](const NamelistDetails &) { return "Namelist"; }, [](const CommonBlockDetails &) { return "CommonBlockDetails"; }, [](const FinalProcDetails &) { return "FinalProc"; }, [](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(); }, [](const auto &) { return false; }, }, details); } } Symbol &Symbol::GetUltimate() { return const_cast(const_cast(this)->GetUltimate()); } const Symbol &Symbol::GetUltimate() const { if (const auto *details{detailsIf()}) { return details->symbol().GetUltimate(); } else if (const auto *details{detailsIf()}) { return details->symbol().GetUltimate(); } else { return *this; } } 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::IsDummy() const { return std::visit( common::visitors{[](const EntityDetails &x) { return x.isDummy(); }, [](const ObjectEntityDetails &x) { return x.isDummy(); }, [](const ProcEntityDetails &x) { return x.isDummy(); }, [](const HostAssocDetails &x) { return x.symbol().IsDummy(); }, [](const auto &) { return false; }}, 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::IsSeparateModuleProc() const { if (attrs().test(Attr::MODULE)) { if (auto *details{detailsIf()}) { return details->isInterface(); } } return false; } ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d) : EntityDetails(d) {} std::ostream &operator<<(std::ostream &os, const EntityDetails &x) { if (x.isDummy()) { os << " dummy"; } if (x.isFuncResult()) { os << " funcResult"; } if (x.type()) { os << " type: " << *x.type(); } if (x.bindName_) { x.bindName_->AsFortran(os << " bindName:"); } return os; } std::ostream &operator<<(std::ostream &os, const ObjectEntityDetails &x) { os << *static_cast(&x); if (!x.shape().empty()) { os << " shape:"; for (const auto &s : x.shape()) { os << ' ' << s; } } if (x.init_) { x.init_->AsFortran(os << " init:"); } return os; } std::ostream &operator<<(std::ostream &os, const AssocEntityDetails &x) { os << *static_cast(&x); if (x.expr().has_value()) { x.expr()->AsFortran(os << ' '); } return os; } std::ostream &operator<<(std::ostream &os, const ProcEntityDetails &x) { if (auto *symbol{x.interface_.symbol()}) { os << ' ' << symbol->name(); } else if (auto *type{x.interface_.type()}) { os << ' ' << *type; } if (x.bindName()) { x.bindName()->AsFortran(os << " bindName:"); } if (x.passName_) { os << " passName:" << *x.passName_; } return os; } std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) { if (x.sequence_) { os << " sequence"; } if (!x.componentNames_.empty()) { os << " components:"; for (auto name : x.componentNames_) { os << ' ' << name.ToString(); } } return os; } static std::ostream &DumpType(std::ostream &os, const Symbol &symbol) { if (const auto *type{symbol.GetType()}) { os << *type << ' '; } return os; } std::ostream &operator<<(std::ostream &os, const Details &details) { os << DetailsToString(details); std::visit( common::visitors{ [&](const UnknownDetails &x) {}, [&](const MainProgramDetails &x) {}, [&](const ModuleDetails &x) { if (x.isSubmodule()) { os << " ("; if (x.ancestor()) { auto &ancestor{x.ancestor()->name()}; os << ancestor; if (x.parent()) { auto &parent{x.parent()->name()}; if (ancestor != parent) { os << ':' << parent; } } } os << ")"; } }, [&](const SubprogramDetails &x) { os << " ("; int n = 0; for (const auto &dummy : x.dummyArgs()) { if (n++ > 0) os << ", "; DumpType(os, *dummy); os << dummy->name(); } os << ')'; if (x.bindName()) { x.bindName()->AsFortran(os << " bindName:"); } if (x.isFunction()) { os << " result("; DumpType(os, x.result()); os << x.result().name() << ')'; } if (x.isInterface()) { os << " interface"; } }, [&](const SubprogramNameDetails &x) { os << ' ' << EnumToString(x.kind()); }, [&](const UseDetails &x) { os << " from " << x.symbol().name() << " in " << x.module().name(); }, [&](const UseErrorDetails &x) { os << " uses:"; for (const auto &[location, module] : x.occurrences()) { os << " from " << module->name() << " at " << location; } }, [](const HostAssocDetails &) {}, [&](const GenericDetails &x) { for (const auto *proc : x.specificProcs()) { os << ' ' << proc->name(); } }, [&](const ProcBindingDetails &x) { os << " => " << x.symbol().name(); if (x.passName()) { os << " passName:" << *x.passName(); } }, [&](const GenericBindingDetails &x) { os << " =>"; char sep{' '}; for (const auto *proc : x.specificProcs()) { os << sep << proc->name(); sep = ','; } }, [&](const NamelistDetails &x) { os << ':'; for (const auto *object : x.objects()) { os << ' ' << object->name(); } }, [&](const CommonBlockDetails &x) { os << ':'; for (const auto *object : x.objects()) { os << ' ' << object->name(); } }, [&](const FinalProcDetails &) {}, [&](const TypeParamDetails &x) { if (x.type()) { os << ' ' << *x.type(); } os << ' ' << common::EnumToString(x.attr()); if (x.init()) { x.init()->AsFortran(os << " init:"); } }, [&](const MiscDetails &x) { os << ' ' << MiscDetails::EnumToString(x.kind()); }, [&](const auto &x) { os << x; }, }, details); return os; } std::ostream &operator<<(std::ostream &o, Symbol::Flag flag) { return o << Symbol::EnumToString(flag); } std::ostream &operator<<(std::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; } std::ostream &operator<<(std::ostream &os, const Symbol &symbol) { os << symbol.name(); if (!symbol.attrs().empty()) { os << ", " << symbol.attrs(); } if (!symbol.flags().empty()) { os << " (" << symbol.flags() << ')'; } 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(std::ostream &os, const Scope &scope) { if (scope.kind() != Scope::Kind::Global) { DumpUniqueName(os, scope.parent()); os << '/'; if (auto *scopeSymbol{scope.symbol()}) { 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. std::ostream &DumpForUnparse( std::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.test(Symbol::Flag::Implicit)) { os << " (implicit)"; } if (symbol.test(Symbol::Flag::LocalityLocal)) { os << " (local)"; } if (symbol.test(Symbol::Flag::LocalityLocalInit)) { os << " (local_init)"; } if (symbol.test(Symbol::Flag::LocalityShared)) { os << " (shared)"; } os << ' ' << symbol.GetDetailsName(); if (const auto *type{symbol.GetType()}) { os << ' ' << *type; } } return os; } Symbol &Symbol::Instantiate( Scope &scope, SemanticsContext &semanticsContext) const { evaluate::FoldingContext foldingContext{semanticsContext.foldingContext()}; CHECK(foldingContext.pdtInstance() != nullptr); const DerivedTypeSpec &instanceSpec{*foldingContext.pdtInstance()}; auto pair{scope.try_emplace(name_, attrs_)}; Symbol &symbol{*pair.first->second}; if (!pair.second) { // Symbol was already present in the scope, which can only happen // in the case of type parameters with actual or default values. CHECK(has()); return symbol; } symbol.attrs_ = attrs_; symbol.flags_ = flags_; std::visit( common::visitors{ [&](const ObjectEntityDetails &that) { symbol.details_ = that; ObjectEntityDetails &details{symbol.get()}; if (DeclTypeSpec * origType{symbol.GetType()}) { if (const DerivedTypeSpec * derived{origType->AsDerived()}) { DerivedTypeSpec newSpec{*derived}; if (test(Flag::ParentComp)) { // Forward all explicit type parameter values from the // derived type spec under instantiation to this parent // component spec when they define type parameters that // pertain to the parent component. for (const auto &pair : instanceSpec.parameters()) { if (scope.find(pair.first) == scope.end()) { newSpec.AddParamValue( pair.first, ParamValue{pair.second}); } } } details.ReplaceType( scope.FindOrInstantiateDerivedType(std::move(newSpec), origType->category(), semanticsContext)); } else if (origType->AsIntrinsic() != nullptr) { const DeclTypeSpec &newType{scope.InstantiateIntrinsicType( *origType, semanticsContext)}; details.ReplaceType(newType); } else { common::die("instantiated component has type that is " "neither intrinsic nor derived"); } } details.set_init( evaluate::Fold(foldingContext, std::move(details.init()))); for (ShapeSpec &dim : details.shape()) { if (dim.lbound().isExplicit()) { dim.lbound().SetExplicit(Fold( foldingContext, std::move(dim.lbound().GetExplicit()))); } if (dim.ubound().isExplicit()) { dim.ubound().SetExplicit(Fold( foldingContext, std::move(dim.ubound().GetExplicit()))); } } // TODO: fold cobounds too once we can represent them }, [&](const ProcBindingDetails &that) { symbol.details_ = that; }, [&](const GenericBindingDetails &that) { symbol.details_ = that; }, [&](const ProcEntityDetails &that) { symbol.details_ = that; }, [&](const TypeParamDetails &that) { // LEN type parameter, or error recovery on a KIND type parameter // with no corresponding actual argument or default symbol.details_ = that; }, [&](const auto &that) { common::die("unexpected details in Symbol::Instantiate"); }, }, details_); return symbol; } const Symbol *Symbol::GetParentComponent(const Scope *scope) const { if (scope == nullptr) { CHECK(scope_ != nullptr); scope = scope_; } return get().GetParentComponent(*scope); } 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; } } void DerivedTypeDetails::add_component(const Symbol &symbol) { if (symbol.test(Symbol::Flag::ParentComp)) { CHECK(componentNames_.empty()); } componentNames_.push_back(symbol.name()); } std::list DerivedTypeDetails::OrderParameterNames( const Symbol &type) const { std::list result; if (const DerivedTypeSpec * spec{type.GetParentTypeSpec()}) { const DerivedTypeDetails &details{ spec->typeSymbol().get()}; result = details.OrderParameterNames(spec->typeSymbol()); } for (const auto &name : paramNames_) { result.push_back(name); } return result; } SymbolList DerivedTypeDetails::OrderParameterDeclarations( const Symbol &type) const { SymbolList result; if (const DerivedTypeSpec * spec{type.GetParentTypeSpec()}) { const DerivedTypeDetails &details{ spec->typeSymbol().get()}; result = details.OrderParameterDeclarations(spec->typeSymbol()); } for (const Symbol *symbol : paramDecls_) { result.push_back(symbol); } return result; } SymbolList DerivedTypeDetails::OrderComponents(const Scope &scope) const { SymbolList result; for (SourceName name : componentNames_) { auto iter{scope.find(name)}; if (iter != scope.cend()) { const Symbol &symbol{*iter->second}; if (symbol.test(Symbol::Flag::ParentComp)) { CHECK(result.empty()); const DerivedTypeSpec &spec{ symbol.get().type()->derivedTypeSpec()}; result = spec.typeSymbol().get().OrderComponents( *spec.scope()); } result.push_back(&symbol); } } return result; } const Symbol *DerivedTypeDetails::GetParentComponent(const Scope &scope) const { if (!componentNames_.empty()) { SourceName extends{componentNames_.front()}; auto iter{scope.find(extends)}; if (iter != scope.cend()) { const Symbol &symbol{*iter->second}; if (symbol.test(Symbol::Flag::ParentComp)) { return &symbol; } } } return nullptr; } void TypeParamDetails::set_type(const DeclTypeSpec &type) { CHECK(type_ == nullptr); type_ = &type; } }