| //===-- 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 == '(' ? "()" : ")"); |
| 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); |
| } |
| } |
| 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()); |
| } |
| } |
| |
| 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 SymbolVector &specificProcs) |
| : specificProcs_{specificProcs} {} |
| |
| 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; |
| } |
| |
| 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) { |
| if (from.specific_) { |
| CHECK(!specific_ || specific_ == from.specific_); |
| specific_ = from.specific_; |
| } |
| if (from.derivedType_) { |
| CHECK(!derivedType_ || derivedType_ == from.derivedType_); |
| derivedType_ = from.derivedType_; |
| } |
| for (const Symbol &symbol : from.specificProcs_) { |
| if (std::find_if(specificProcs_.begin(), specificProcs_.end(), |
| [&](const Symbol &mySymbol) { return &mySymbol == &symbol; }) == |
| specificProcs_.end()) { |
| specificProcs_.push_back(symbol); |
| } |
| } |
| } |
| |
| // 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 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<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 &) { |
| auto *derived{detailsIf<DerivedTypeDetails>()}; |
| return derived && derived->isForwardReferenced(); |
| }, |
| [](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::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<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); |
| 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 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 " << x.module().name(); |
| }, |
| [&](const UseErrorDetails &x) { |
| os << " uses:"; |
| for (const auto &[location, module] : x.occurrences()) { |
| os << " from " << module->GetName().value() << " at " << location; |
| } |
| }, |
| [](const HostAssocDetails &) {}, |
| [&](const GenericDetails &x) { |
| os << ' ' << x.kind().ToString(); |
| DumpBool(os, "(specific)", x.specific() != nullptr); |
| DumpBool(os, "(derivedType)", x.derivedType() != nullptr); |
| os << " procs:"; |
| DumpSymbolVector(os, x.specificProcs()); |
| }, |
| [&](const ProcBindingDetails &x) { |
| os << " => " << x.symbol().name(); |
| DumpOptional(os, "passName", x.passName()); |
| }, |
| [&](const NamelistDetails &x) { |
| os << ':'; |
| DumpSymbolVector(os, x.objects()); |
| }, |
| [&](const CommonBlockDetails &x) { |
| os << ':'; |
| for (const Symbol &object : x.objects()) { |
| os << ' ' << object.name(); |
| } |
| }, |
| [&](const FinalProcDetails &) {}, |
| [&](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() << ')'; |
| } |
| 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 (!scope) { |
| scope = scope_; |
| } |
| return dtDetails->GetParentComponent(DEREF(scope)); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| // Utility routine for InstantiateComponent(): applies type |
| // parameter values to an intrinsic type spec. |
| static const DeclTypeSpec &InstantiateIntrinsicType(Scope &scope, |
| const DeclTypeSpec &spec, SemanticsContext &semanticsContext) { |
| const IntrinsicTypeSpec &intrinsic{DEREF(spec.AsIntrinsic())}; |
| if (evaluate::ToInt64(intrinsic.kind())) { |
| return spec; // KIND is already a known constant |
| } |
| // The expression was not originally constant, but now it must be so |
| // in the context of a parameterized derived type instantiation. |
| KindExpr copy{intrinsic.kind()}; |
| evaluate::FoldingContext &foldingContext{semanticsContext.foldingContext()}; |
| copy = evaluate::Fold(foldingContext, std::move(copy)); |
| int kind{semanticsContext.GetDefaultKind(intrinsic.category())}; |
| if (auto value{evaluate::ToInt64(copy)}) { |
| if (evaluate::IsValidKindOfIntrinsicType(intrinsic.category(), *value)) { |
| kind = *value; |
| } else { |
| foldingContext.messages().Say( |
| "KIND parameter value (%jd) of intrinsic type %s " |
| "did not resolve to a supported value"_err_en_US, |
| *value, |
| parser::ToUpperCaseLetters( |
| common::EnumToString(intrinsic.category()))); |
| } |
| } |
| switch (spec.category()) { |
| case DeclTypeSpec::Numeric: |
| return scope.MakeNumericType(intrinsic.category(), KindExpr{kind}); |
| case DeclTypeSpec::Logical: // |
| return scope.MakeLogicalType(KindExpr{kind}); |
| case DeclTypeSpec::Character: |
| return scope.MakeCharacterType( |
| ParamValue{spec.characterTypeSpec().length()}, KindExpr{kind}); |
| default: |
| CRASH_NO_CASE; |
| } |
| } |
| |
| Symbol &Symbol::InstantiateComponent( |
| Scope &scope, SemanticsContext &context) const { |
| auto &foldingContext{context.foldingContext()}; |
| auto pair{scope.try_emplace(name(), attrs())}; |
| Symbol &result{*pair.first->second}; |
| if (!pair.second) { |
| // Symbol was already present in the scope, which can only happen |
| // in the case of type parameters. |
| CHECK(has<TypeParamDetails>()); |
| return result; |
| } |
| result.attrs() = attrs(); |
| result.flags() = flags(); |
| result.set_details(common::Clone(details())); |
| if (auto *details{result.detailsIf<ObjectEntityDetails>()}) { |
| if (DeclTypeSpec * origType{result.GetType()}) { |
| if (const DerivedTypeSpec * derived{origType->AsDerived()}) { |
| DerivedTypeSpec newSpec{*derived}; |
| newSpec.CookParameters(foldingContext); // enables AddParamValue() |
| if (test(Symbol::Flag::ParentComp)) { |
| // Forward any explicit type parameter values from the |
| // derived type spec under instantiation that define type parameters |
| // of the parent component to the derived type spec of the |
| // parent component. |
| const DerivedTypeSpec &instanceSpec{ |
| DEREF(foldingContext.pdtInstance())}; |
| for (const auto &[name, value] : instanceSpec.parameters()) { |
| if (scope.find(name) == scope.end()) { |
| newSpec.AddParamValue(name, ParamValue{value}); |
| } |
| } |
| } |
| details->ReplaceType(FindOrInstantiateDerivedType( |
| scope, std::move(newSpec), context, origType->category())); |
| } else if (origType->AsIntrinsic()) { |
| details->ReplaceType( |
| InstantiateIntrinsicType(scope, *origType, context)); |
| } else if (origType->category() != DeclTypeSpec::ClassStar) { |
| DIE("instantiated component has type that is " |
| "neither intrinsic, derived, nor CLASS(*)"); |
| } |
| } |
| 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()))); |
| } |
| } |
| for (ShapeSpec &dim : details->coshape()) { |
| 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()))); |
| } |
| } |
| } else if (!attrs_.test(Attr::NOPASS)) { |
| std::visit( |
| [&result](const auto &x) { |
| using Ty = std::decay_t<decltype(x)>; |
| if constexpr (std::is_base_of_v<WithPassArg, Ty>) { |
| if (auto passName{x.passName()}) { |
| result.get<Ty>().set_passName(*passName); |
| } |
| } |
| }, |
| details_); |
| } |
| return result; |
| } |
| |
| 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; |
| } |
| |
| 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 |