| //===-- include/flang/Semantics/symbol.h ------------------------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef FORTRAN_SEMANTICS_SYMBOL_H_ |
| #define FORTRAN_SEMANTICS_SYMBOL_H_ |
| |
| #include "type.h" |
| #include "flang/Common/Fortran.h" |
| #include "flang/Common/enum-set.h" |
| #include "flang/Common/reference.h" |
| #include "llvm/ADT/DenseMapInfo.h" |
| #include <array> |
| #include <functional> |
| #include <list> |
| #include <optional> |
| #include <set> |
| #include <vector> |
| |
| namespace llvm { |
| class raw_ostream; |
| } |
| |
| namespace Fortran::semantics { |
| |
| /// A Symbol consists of common information (name, owner, and attributes) |
| /// and details information specific to the kind of symbol, represented by the |
| /// *Details classes. |
| |
| class Scope; |
| class Symbol; |
| class ProgramTree; |
| |
| using SymbolRef = common::Reference<const Symbol>; |
| using SymbolVector = std::vector<SymbolRef>; |
| using MutableSymbolRef = common::Reference<Symbol>; |
| using MutableSymbolVector = std::vector<MutableSymbolRef>; |
| |
| // A module or submodule. |
| class ModuleDetails { |
| public: |
| ModuleDetails(bool isSubmodule = false) : isSubmodule_{isSubmodule} {} |
| bool isSubmodule() const { return isSubmodule_; } |
| const Scope *scope() const { return scope_; } |
| const Scope *ancestor() const; // for submodule; nullptr for module |
| const Scope *parent() const; // for submodule; nullptr for module |
| void set_scope(const Scope *); |
| |
| private: |
| bool isSubmodule_; |
| const Scope *scope_{nullptr}; |
| }; |
| |
| class MainProgramDetails { |
| public: |
| private: |
| }; |
| |
| class WithBindName { |
| public: |
| const std::string *bindName() const { |
| return bindName_ ? &*bindName_ : nullptr; |
| } |
| void set_bindName(std::string &&name) { bindName_ = std::move(name); } |
| |
| private: |
| std::optional<std::string> bindName_; |
| }; |
| |
| class SubprogramDetails : public WithBindName { |
| public: |
| bool isFunction() const { return result_ != nullptr; } |
| bool isInterface() const { return isInterface_; } |
| void set_isInterface(bool value = true) { isInterface_ = value; } |
| bool isDummy() const { return isDummy_; } |
| void set_isDummy(bool value = true) { isDummy_ = value; } |
| Scope *entryScope() { return entryScope_; } |
| const Scope *entryScope() const { return entryScope_; } |
| void set_entryScope(Scope &scope) { entryScope_ = &scope; } |
| const Symbol &result() const { |
| CHECK(isFunction()); |
| return *result_; |
| } |
| void set_result(Symbol &result) { |
| CHECK(!result_); |
| result_ = &result; |
| } |
| const std::vector<Symbol *> &dummyArgs() const { return dummyArgs_; } |
| void add_dummyArg(Symbol &symbol) { dummyArgs_.push_back(&symbol); } |
| void add_alternateReturn() { dummyArgs_.push_back(nullptr); } |
| const MaybeExpr &stmtFunction() const { return stmtFunction_; } |
| void set_stmtFunction(SomeExpr &&expr) { stmtFunction_ = std::move(expr); } |
| |
| private: |
| bool isInterface_{false}; // true if this represents an interface-body |
| bool isDummy_{false}; // true when interface of dummy procedure |
| std::vector<Symbol *> dummyArgs_; // nullptr -> alternate return indicator |
| Symbol *result_{nullptr}; |
| Scope *entryScope_{nullptr}; // if ENTRY, points to subprogram's scope |
| MaybeExpr stmtFunction_; |
| friend llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &, const SubprogramDetails &); |
| }; |
| |
| // For SubprogramNameDetails, the kind indicates whether it is the name |
| // of a module subprogram or internal subprogram. |
| ENUM_CLASS(SubprogramKind, Module, Internal) |
| |
| // Symbol with SubprogramNameDetails is created when we scan for module and |
| // internal procedure names, to record that there is a subprogram with this |
| // name. Later they are replaced by SubprogramDetails with dummy and result |
| // type information. |
| class SubprogramNameDetails { |
| public: |
| SubprogramNameDetails(SubprogramKind kind, ProgramTree &node) |
| : kind_{kind}, node_{node} {} |
| SubprogramNameDetails() = delete; |
| SubprogramKind kind() const { return kind_; } |
| ProgramTree &node() const { return *node_; } |
| |
| private: |
| SubprogramKind kind_; |
| common::Reference<ProgramTree> node_; |
| }; |
| |
| // A name from an entity-decl -- could be object or function. |
| class EntityDetails : public WithBindName { |
| public: |
| explicit EntityDetails(bool isDummy = false) : isDummy_{isDummy} {} |
| const DeclTypeSpec *type() const { return type_; } |
| void set_type(const DeclTypeSpec &); |
| void ReplaceType(const DeclTypeSpec &); |
| bool isDummy() const { return isDummy_; } |
| void set_isDummy(bool value = true) { isDummy_ = value; } |
| bool isFuncResult() const { return isFuncResult_; } |
| void set_funcResult(bool x) { isFuncResult_ = x; } |
| |
| private: |
| bool isDummy_{false}; |
| bool isFuncResult_{false}; |
| const DeclTypeSpec *type_{nullptr}; |
| friend llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &, const EntityDetails &); |
| }; |
| |
| // Symbol is associated with a name or expression in a SELECT TYPE or ASSOCIATE. |
| class AssocEntityDetails : public EntityDetails { |
| public: |
| AssocEntityDetails() {} |
| explicit AssocEntityDetails(SomeExpr &&expr) : expr_{std::move(expr)} {} |
| AssocEntityDetails(const AssocEntityDetails &) = default; |
| AssocEntityDetails(AssocEntityDetails &&) = default; |
| AssocEntityDetails &operator=(const AssocEntityDetails &) = default; |
| AssocEntityDetails &operator=(AssocEntityDetails &&) = default; |
| const MaybeExpr &expr() const { return expr_; } |
| void set_rank(int rank); |
| std::optional<int> rank() const { return rank_; } |
| |
| private: |
| MaybeExpr expr_; |
| std::optional<int> rank_; |
| }; |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &, const AssocEntityDetails &); |
| |
| // An entity known to be an object. |
| class ObjectEntityDetails : public EntityDetails { |
| public: |
| explicit ObjectEntityDetails(EntityDetails &&); |
| ObjectEntityDetails(const ObjectEntityDetails &) = default; |
| ObjectEntityDetails &operator=(const ObjectEntityDetails &) = default; |
| ObjectEntityDetails(bool isDummy = false) : EntityDetails(isDummy) {} |
| MaybeExpr &init() { return init_; } |
| const MaybeExpr &init() const { return init_; } |
| void set_init(MaybeExpr &&expr) { init_ = std::move(expr); } |
| ArraySpec &shape() { return shape_; } |
| const ArraySpec &shape() const { return shape_; } |
| ArraySpec &coshape() { return coshape_; } |
| const ArraySpec &coshape() const { return coshape_; } |
| void set_shape(const ArraySpec &); |
| void set_coshape(const ArraySpec &); |
| const Symbol *commonBlock() const { return commonBlock_; } |
| void set_commonBlock(const Symbol &commonBlock) { |
| commonBlock_ = &commonBlock; |
| } |
| bool IsArray() const { return !shape_.empty(); } |
| bool IsCoarray() const { return !coshape_.empty(); } |
| bool IsAssumedShape() const { return isDummy() && shape_.IsAssumedShape(); } |
| bool IsDeferredShape() const { |
| return !isDummy() && shape_.IsDeferredShape(); |
| } |
| bool IsAssumedSize() const { return isDummy() && shape_.IsAssumedSize(); } |
| bool IsAssumedRank() const { return isDummy() && shape_.IsAssumedRank(); } |
| |
| private: |
| MaybeExpr init_; |
| ArraySpec shape_; |
| ArraySpec coshape_; |
| const Symbol *commonBlock_{nullptr}; // common block this object is in |
| friend llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &, const ObjectEntityDetails &); |
| }; |
| |
| // Mixin for details with passed-object dummy argument. |
| // If a procedure pointer component or type-bound procedure does not have |
| // the NOPASS attribute on its symbol, then PASS is assumed; the name |
| // is optional; if it is missing, the first dummy argument of the procedure's |
| // interface is the passed-object dummy argument. |
| class WithPassArg { |
| public: |
| std::optional<SourceName> passName() const { return passName_; } |
| void set_passName(const SourceName &passName) { passName_ = passName; } |
| |
| private: |
| std::optional<SourceName> passName_; |
| }; |
| |
| // A procedure pointer, dummy procedure, or external procedure |
| class ProcEntityDetails : public EntityDetails, public WithPassArg { |
| public: |
| ProcEntityDetails() = default; |
| explicit ProcEntityDetails(EntityDetails &&d); |
| |
| const ProcInterface &interface() const { return interface_; } |
| ProcInterface &interface() { return interface_; } |
| void set_interface(const ProcInterface &interface) { interface_ = interface; } |
| bool IsInterfaceSet() { |
| return interface_.symbol() != nullptr || interface_.type() != nullptr; |
| } |
| inline bool HasExplicitInterface() const; |
| |
| // Be advised: !init().has_value() => uninitialized pointer, |
| // while *init() == nullptr => explicit NULL() initialization. |
| std::optional<const Symbol *> init() const { return init_; } |
| void set_init(const Symbol &symbol) { init_ = &symbol; } |
| void set_init(std::nullptr_t) { init_ = nullptr; } |
| |
| private: |
| ProcInterface interface_; |
| std::optional<const Symbol *> init_; |
| friend llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &, const ProcEntityDetails &); |
| }; |
| |
| // These derived type details represent the characteristics of a derived |
| // type definition that are shared by all instantiations of that type. |
| // The DerivedTypeSpec instances whose type symbols share these details |
| // each own a scope into which the components' symbols have been cloned |
| // and specialized for each distinct set of type parameter values. |
| class DerivedTypeDetails { |
| public: |
| const std::list<SourceName> ¶mNames() const { return paramNames_; } |
| const SymbolVector ¶mDecls() const { return paramDecls_; } |
| bool sequence() const { return sequence_; } |
| std::map<SourceName, SymbolRef> &finals() { return finals_; } |
| const std::map<SourceName, SymbolRef> &finals() const { return finals_; } |
| bool isForwardReferenced() const { return isForwardReferenced_; } |
| void add_paramName(const SourceName &name) { paramNames_.push_back(name); } |
| void add_paramDecl(const Symbol &symbol) { paramDecls_.push_back(symbol); } |
| void add_component(const Symbol &); |
| void set_sequence(bool x = true) { sequence_ = x; } |
| void set_isForwardReferenced(bool value) { isForwardReferenced_ = value; } |
| const std::list<SourceName> &componentNames() const { |
| return componentNames_; |
| } |
| |
| // If this derived type extends another, locate the parent component's symbol. |
| const Symbol *GetParentComponent(const Scope &) const; |
| |
| std::optional<SourceName> GetParentComponentName() const { |
| if (componentNames_.empty()) { |
| return std::nullopt; |
| } else { |
| return componentNames_.front(); |
| } |
| } |
| |
| const Symbol *GetFinalForRank(int) const; |
| |
| private: |
| // These are (1) the names of the derived type parameters in the order |
| // in which they appear on the type definition statement(s), and (2) the |
| // symbols that correspond to those names in the order in which their |
| // declarations appear in the derived type definition(s). |
| std::list<SourceName> paramNames_; |
| SymbolVector paramDecls_; |
| // These are the names of the derived type's components in component |
| // order. A parent component, if any, appears first in this list. |
| std::list<SourceName> componentNames_; |
| std::map<SourceName, SymbolRef> finals_; // FINAL :: subr |
| bool sequence_{false}; |
| bool isForwardReferenced_{false}; |
| friend llvm::raw_ostream &operator<<( |
| llvm::raw_ostream &, const DerivedTypeDetails &); |
| }; |
| |
| class ProcBindingDetails : public WithPassArg { |
| public: |
| explicit ProcBindingDetails(const Symbol &symbol) : symbol_{symbol} {} |
| const Symbol &symbol() const { return symbol_; } |
| |
| private: |
| SymbolRef symbol_; // procedure bound to; may be forward |
| }; |
| |
| class NamelistDetails { |
| public: |
| const SymbolVector &objects() const { return objects_; } |
| void add_object(const Symbol &object) { objects_.push_back(object); } |
| void add_objects(const SymbolVector &objects) { |
| objects_.insert(objects_.end(), objects.begin(), objects.end()); |
| } |
| |
| private: |
| SymbolVector objects_; |
| }; |
| |
| class CommonBlockDetails : public WithBindName { |
| public: |
| MutableSymbolVector &objects() { return objects_; } |
| const MutableSymbolVector &objects() const { return objects_; } |
| void add_object(Symbol &object) { objects_.emplace_back(object); } |
| std::size_t alignment() const { return alignment_; } |
| void set_alignment(std::size_t alignment) { alignment_ = alignment; } |
| |
| private: |
| MutableSymbolVector objects_; |
| std::size_t alignment_{0}; // required alignment in bytes |
| }; |
| |
| class MiscDetails { |
| public: |
| ENUM_CLASS(Kind, None, ConstructName, ScopeName, PassName, ComplexPartRe, |
| ComplexPartIm, KindParamInquiry, LenParamInquiry, SelectRankAssociateName, |
| SelectTypeAssociateName, TypeBoundDefinedOp); |
| MiscDetails(Kind kind) : kind_{kind} {} |
| Kind kind() const { return kind_; } |
| |
| private: |
| Kind kind_; |
| }; |
| |
| class TypeParamDetails { |
| public: |
| explicit TypeParamDetails(common::TypeParamAttr attr) : attr_{attr} {} |
| TypeParamDetails(const TypeParamDetails &) = default; |
| common::TypeParamAttr attr() const { return attr_; } |
| MaybeIntExpr &init() { return init_; } |
| const MaybeIntExpr &init() const { return init_; } |
| void set_init(MaybeIntExpr &&expr) { init_ = std::move(expr); } |
| const DeclTypeSpec *type() const { return type_; } |
| void set_type(const DeclTypeSpec &); |
| void ReplaceType(const DeclTypeSpec &); |
| |
| private: |
| common::TypeParamAttr attr_; |
| MaybeIntExpr init_; |
| const DeclTypeSpec *type_{nullptr}; |
| }; |
| |
| // Record the USE of a symbol: location is where (USE statement or renaming); |
| // symbol is in the USEd module. |
| class UseDetails { |
| public: |
| UseDetails(const SourceName &location, const Symbol &symbol) |
| : location_{location}, symbol_{symbol} {} |
| const SourceName &location() const { return location_; } |
| const Symbol &symbol() const { return symbol_; } |
| |
| private: |
| SourceName location_; |
| SymbolRef symbol_; |
| }; |
| |
| // A symbol with ambiguous use-associations. Record where they were so |
| // we can report the error if it is used. |
| class UseErrorDetails { |
| public: |
| UseErrorDetails(const UseDetails &); |
| UseErrorDetails &add_occurrence(const SourceName &, const Scope &); |
| using listType = std::list<std::pair<SourceName, const Scope *>>; |
| const listType occurrences() const { return occurrences_; }; |
| |
| private: |
| listType occurrences_; |
| }; |
| |
| // A symbol host-associated from an enclosing scope. |
| class HostAssocDetails { |
| public: |
| HostAssocDetails(const Symbol &symbol) : symbol_{symbol} {} |
| const Symbol &symbol() const { return symbol_; } |
| bool implicitOrSpecExprError{false}; |
| bool implicitOrExplicitTypeError{false}; |
| |
| private: |
| SymbolRef symbol_; |
| }; |
| |
| // A GenericKind is one of: generic name, defined operator, |
| // defined assignment, intrinsic operator, or defined I/O. |
| struct GenericKind { |
| ENUM_CLASS(OtherKind, Name, DefinedOp, Assignment, Concat) |
| ENUM_CLASS(DefinedIo, // defined io |
| ReadFormatted, ReadUnformatted, WriteFormatted, WriteUnformatted) |
| GenericKind() : u{OtherKind::Name} {} |
| template <typename T> GenericKind(const T &x) { u = x; } |
| bool IsName() const { return Is(OtherKind::Name); } |
| bool IsAssignment() const { return Is(OtherKind::Assignment); } |
| bool IsDefinedOperator() const { return Is(OtherKind::DefinedOp); } |
| bool IsIntrinsicOperator() const; |
| bool IsOperator() const; |
| std::string ToString() const; |
| std::variant<OtherKind, common::NumericOperator, common::LogicalOperator, |
| common::RelationalOperator, DefinedIo> |
| u; |
| |
| private: |
| template <typename T> bool Has() const { |
| return std::holds_alternative<T>(u); |
| } |
| bool Is(OtherKind) const; |
| }; |
| |
| // A generic interface or type-bound generic. |
| class GenericDetails { |
| public: |
| GenericDetails() {} |
| |
| GenericKind kind() const { return kind_; } |
| void set_kind(GenericKind kind) { kind_ = kind; } |
| |
| const SymbolVector &specificProcs() const { return specificProcs_; } |
| const std::vector<SourceName> &bindingNames() const { return bindingNames_; } |
| void AddSpecificProc(const Symbol &, SourceName bindingName); |
| const SymbolVector &uses() const { return uses_; } |
| |
| // specific and derivedType indicate a specific procedure or derived type |
| // with the same name as this generic. Only one of them may be set. |
| Symbol *specific() { return specific_; } |
| const Symbol *specific() const { return specific_; } |
| void set_specific(Symbol &specific); |
| Symbol *derivedType() { return derivedType_; } |
| const Symbol *derivedType() const { return derivedType_; } |
| void set_derivedType(Symbol &derivedType); |
| void AddUse(const Symbol &); |
| |
| // Copy in specificProcs, specific, and derivedType from another generic |
| void CopyFrom(const GenericDetails &); |
| |
| // Check that specific is one of the specificProcs. If not, return the |
| // specific as a raw pointer. |
| const Symbol *CheckSpecific() const; |
| Symbol *CheckSpecific(); |
| |
| private: |
| GenericKind kind_; |
| // all of the specific procedures for this generic |
| SymbolVector specificProcs_; |
| std::vector<SourceName> bindingNames_; |
| // Symbols used from other modules merged into this one |
| SymbolVector uses_; |
| // a specific procedure with the same name as this generic, if any |
| Symbol *specific_{nullptr}; |
| // a derived type with the same name as this generic, if any |
| Symbol *derivedType_{nullptr}; |
| }; |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &, const GenericDetails &); |
| |
| class UnknownDetails {}; |
| |
| using Details = std::variant<UnknownDetails, MainProgramDetails, ModuleDetails, |
| SubprogramDetails, SubprogramNameDetails, EntityDetails, |
| ObjectEntityDetails, ProcEntityDetails, AssocEntityDetails, |
| DerivedTypeDetails, UseDetails, UseErrorDetails, HostAssocDetails, |
| GenericDetails, ProcBindingDetails, NamelistDetails, CommonBlockDetails, |
| TypeParamDetails, MiscDetails>; |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Details &); |
| std::string DetailsToString(const Details &); |
| |
| class Symbol { |
| public: |
| ENUM_CLASS(Flag, |
| Function, // symbol is a function |
| Subroutine, // symbol is a subroutine |
| StmtFunction, // symbol is a statement function (Function is set too) |
| Implicit, // symbol is implicitly typed |
| ImplicitOrError, // symbol must be implicitly typed or it's an error |
| ModFile, // symbol came from .mod file |
| ParentComp, // symbol is the "parent component" of an extended type |
| CrayPointer, CrayPointee, |
| LocalityLocal, // named in LOCAL locality-spec |
| LocalityLocalInit, // named in LOCAL_INIT locality-spec |
| LocalityShared, // named in SHARED locality-spec |
| InDataStmt, // initialized in a DATA statement |
| InNamelist, // flag is set if the symbol is in Namelist statement |
| CompilerCreated, |
| // OpenACC data-sharing attribute |
| AccPrivate, AccFirstPrivate, AccShared, |
| // OpenACC data-mapping attribute |
| AccCopyIn, AccCopyOut, AccCreate, AccDelete, AccPresent, |
| // OpenACC miscellaneous flags |
| AccCommonBlock, AccThreadPrivate, AccReduction, AccNone, AccPreDetermined, |
| // OpenMP data-sharing attribute |
| OmpShared, OmpPrivate, OmpLinear, OmpFirstPrivate, OmpLastPrivate, |
| // OpenMP data-mapping attribute |
| OmpMapTo, OmpMapFrom, OmpMapAlloc, OmpMapRelease, OmpMapDelete, |
| // OpenMP data-copying attribute |
| OmpCopyIn, OmpCopyPrivate, |
| // OpenMP miscellaneous flags |
| OmpCommonBlock, OmpReduction, OmpAligned, OmpNontemporal, OmpAllocate, |
| OmpDeclarativeAllocateDirective, OmpExecutableAllocateDirective, |
| OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate, OmpDeclareReduction, |
| OmpFlushed, OmpCriticalLock, OmpIfSpecified, OmpNone, OmpPreDetermined); |
| using Flags = common::EnumSet<Flag, Flag_enumSize>; |
| |
| const Scope &owner() const { return *owner_; } |
| const SourceName &name() const { return name_; } |
| Attrs &attrs() { return attrs_; } |
| const Attrs &attrs() const { return attrs_; } |
| Flags &flags() { return flags_; } |
| const Flags &flags() const { return flags_; } |
| bool test(Flag flag) const { return flags_.test(flag); } |
| void set(Flag flag, bool value = true) { flags_.set(flag, value); } |
| // The Scope introduced by this symbol, if any. |
| Scope *scope() { return scope_; } |
| const Scope *scope() const { return scope_; } |
| void set_scope(Scope *scope) { scope_ = scope; } |
| std::size_t size() const { return size_; } |
| void set_size(std::size_t size) { size_ = size; } |
| std::size_t offset() const { return offset_; } |
| void set_offset(std::size_t offset) { offset_ = offset; } |
| // Give the symbol a name with a different source location but same chars. |
| void ReplaceName(const SourceName &); |
| |
| // Does symbol have this type of details? |
| template <typename D> bool has() const { |
| return std::holds_alternative<D>(details_); |
| } |
| |
| // Return a non-owning pointer to details if it is type D, else nullptr. |
| template <typename D> D *detailsIf() { return std::get_if<D>(&details_); } |
| template <typename D> const D *detailsIf() const { |
| return std::get_if<D>(&details_); |
| } |
| |
| // Return a reference to the details which must be of type D. |
| template <typename D> D &get() { |
| return const_cast<D &>(const_cast<const Symbol *>(this)->get<D>()); |
| } |
| template <typename D> const D &get() const { |
| const auto *p{detailsIf<D>()}; |
| CHECK(p); |
| return *p; |
| } |
| |
| Details &details() { return details_; } |
| const Details &details() const { return details_; } |
| // Assign the details of the symbol from one of the variants. |
| // Only allowed in certain cases. |
| void set_details(Details &&); |
| |
| // Can the details of this symbol be replaced with the given details? |
| bool CanReplaceDetails(const Details &details) const; |
| |
| // Follow use-associations and host-associations to get the ultimate entity. |
| inline Symbol &GetUltimate(); |
| inline const Symbol &GetUltimate() const; |
| |
| inline DeclTypeSpec *GetType(); |
| inline const DeclTypeSpec *GetType() const; |
| void SetType(const DeclTypeSpec &); |
| |
| const std::string *GetBindName() const; |
| void SetBindName(std::string &&); |
| bool IsFuncResult() const; |
| bool IsObjectArray() const; |
| bool IsSubprogram() const; |
| bool IsFromModFile() const; |
| bool HasExplicitInterface() const { |
| return std::visit(common::visitors{ |
| [](const SubprogramDetails &) { return true; }, |
| [](const SubprogramNameDetails &) { return true; }, |
| [&](const ProcEntityDetails &x) { |
| return attrs_.test(Attr::INTRINSIC) || |
| x.HasExplicitInterface(); |
| }, |
| [](const ProcBindingDetails &x) { |
| return x.symbol().HasExplicitInterface(); |
| }, |
| [](const UseDetails &x) { |
| return x.symbol().HasExplicitInterface(); |
| }, |
| [](const HostAssocDetails &x) { |
| return x.symbol().HasExplicitInterface(); |
| }, |
| [](const auto &) { return false; }, |
| }, |
| details_); |
| } |
| |
| bool operator==(const Symbol &that) const { return this == &that; } |
| bool operator!=(const Symbol &that) const { return !(*this == that); } |
| |
| int Rank() const { |
| return std::visit( |
| common::visitors{ |
| [](const SubprogramDetails &sd) { |
| return sd.isFunction() ? sd.result().Rank() : 0; |
| }, |
| [](const GenericDetails &) { |
| return 0; /*TODO*/ |
| }, |
| [](const ProcBindingDetails &x) { return x.symbol().Rank(); }, |
| [](const UseDetails &x) { return x.symbol().Rank(); }, |
| [](const HostAssocDetails &x) { return x.symbol().Rank(); }, |
| [](const ObjectEntityDetails &oed) { return oed.shape().Rank(); }, |
| [](const AssocEntityDetails &aed) { |
| if (const auto &expr{aed.expr()}) { |
| if (auto assocRank{aed.rank()}) { |
| return *assocRank; |
| } else { |
| return expr->Rank(); |
| } |
| } else { |
| return 0; |
| } |
| }, |
| [](const auto &) { return 0; }, |
| }, |
| details_); |
| } |
| |
| int Corank() const { |
| return std::visit( |
| common::visitors{ |
| [](const SubprogramDetails &sd) { |
| return sd.isFunction() ? sd.result().Corank() : 0; |
| }, |
| [](const GenericDetails &) { |
| return 0; /*TODO*/ |
| }, |
| [](const UseDetails &x) { return x.symbol().Corank(); }, |
| [](const HostAssocDetails &x) { return x.symbol().Corank(); }, |
| [](const ObjectEntityDetails &oed) { return oed.coshape().Rank(); }, |
| [](const auto &) { return 0; }, |
| }, |
| details_); |
| } |
| |
| // If there is a parent component, return a pointer to its derived type spec. |
| // The Scope * argument defaults to this->scope_ but should be overridden |
| // for a parameterized derived type instantiation with the instance's scope. |
| const DerivedTypeSpec *GetParentTypeSpec(const Scope * = nullptr) const; |
| |
| SemanticsContext &GetSemanticsContext() const; |
| |
| private: |
| const Scope *owner_; |
| SourceName name_; |
| Attrs attrs_; |
| Flags flags_; |
| Scope *scope_{nullptr}; |
| std::size_t size_{0}; // size in bytes |
| std::size_t offset_{0}; // byte offset in scope or common block |
| Details details_; |
| |
| Symbol() {} // only created in class Symbols |
| const std::string GetDetailsName() const; |
| friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Symbol &); |
| friend llvm::raw_ostream &DumpForUnparse( |
| llvm::raw_ostream &, const Symbol &, bool); |
| |
| // If a derived type's symbol refers to an extended derived type, |
| // return the parent component's symbol. The scope of the derived type |
| // can be overridden. |
| const Symbol *GetParentComponent(const Scope * = nullptr) const; |
| |
| template <std::size_t> friend class Symbols; |
| template <class, std::size_t> friend struct std::array; |
| }; |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &, Symbol::Flag); |
| |
| // Manage memory for all symbols. BLOCK_SIZE symbols at a time are allocated. |
| // Make() returns a reference to the next available one. They are never |
| // deleted. |
| template <std::size_t BLOCK_SIZE> class Symbols { |
| public: |
| Symbol &Make(const Scope &owner, const SourceName &name, const Attrs &attrs, |
| Details &&details) { |
| Symbol &symbol = Get(); |
| symbol.owner_ = &owner; |
| symbol.name_ = name; |
| symbol.attrs_ = attrs; |
| symbol.details_ = std::move(details); |
| return symbol; |
| } |
| |
| private: |
| using blockType = std::array<Symbol, BLOCK_SIZE>; |
| std::list<blockType *> blocks_; |
| std::size_t nextIndex_{0}; |
| blockType *currBlock_{nullptr}; |
| |
| Symbol &Get() { |
| if (nextIndex_ == 0) { |
| blocks_.push_back(new blockType()); |
| currBlock_ = blocks_.back(); |
| } |
| Symbol &result = (*currBlock_)[nextIndex_]; |
| if (++nextIndex_ >= BLOCK_SIZE) { |
| nextIndex_ = 0; // allocate a new block next time |
| } |
| return result; |
| } |
| }; |
| |
| // Define a few member functions here in the header so that they |
| // can be used by lib/Evaluate without inducing a dependence cycle |
| // between the two shared libraries. |
| |
| inline bool ProcEntityDetails::HasExplicitInterface() const { |
| if (auto *symbol{interface_.symbol()}) { |
| return symbol->HasExplicitInterface(); |
| } |
| return false; |
| } |
| |
| inline Symbol &Symbol::GetUltimate() { |
| return const_cast<Symbol &>(const_cast<const Symbol *>(this)->GetUltimate()); |
| } |
| inline const Symbol &Symbol::GetUltimate() const { |
| if (const auto *details{detailsIf<UseDetails>()}) { |
| return details->symbol().GetUltimate(); |
| } else if (const auto *details{detailsIf<HostAssocDetails>()}) { |
| return details->symbol().GetUltimate(); |
| } else { |
| return *this; |
| } |
| } |
| |
| inline DeclTypeSpec *Symbol::GetType() { |
| return const_cast<DeclTypeSpec *>( |
| const_cast<const Symbol *>(this)->GetType()); |
| } |
| inline const DeclTypeSpec *Symbol::GetType() const { |
| return std::visit( |
| common::visitors{ |
| [](const EntityDetails &x) { return x.type(); }, |
| [](const ObjectEntityDetails &x) { return x.type(); }, |
| [](const AssocEntityDetails &x) { return x.type(); }, |
| [](const SubprogramDetails &x) { |
| return x.isFunction() ? x.result().GetType() : nullptr; |
| }, |
| [](const ProcEntityDetails &x) { |
| const Symbol *symbol{x.interface().symbol()}; |
| return symbol ? symbol->GetType() : x.interface().type(); |
| }, |
| [](const ProcBindingDetails &x) { return x.symbol().GetType(); }, |
| [](const TypeParamDetails &x) { return x.type(); }, |
| [](const UseDetails &x) { return x.symbol().GetType(); }, |
| [](const HostAssocDetails &x) { return x.symbol().GetType(); }, |
| [](const auto &) -> const DeclTypeSpec * { return nullptr; }, |
| }, |
| details_); |
| } |
| |
| // Sets and maps keyed by Symbols |
| |
| struct SymbolAddressCompare { |
| bool operator()(const SymbolRef &x, const SymbolRef &y) const { |
| return &*x < &*y; |
| } |
| bool operator()(const MutableSymbolRef &x, const MutableSymbolRef &y) const { |
| return &*x < &*y; |
| } |
| }; |
| |
| // Symbol comparison is usually based on the order of cooked source |
| // stream creation and, when both are from the same cooked source, |
| // their positions in that cooked source stream. |
| // Don't use this comparator or OrderedSymbolSet to hold |
| // Symbols that might be subject to ReplaceName(). |
| struct SymbolSourcePositionCompare { |
| // These functions are implemented in Evaluate/tools.cpp to |
| // satisfy complicated shared library interdependency. |
| bool operator()(const SymbolRef &, const SymbolRef &) const; |
| bool operator()(const MutableSymbolRef &, const MutableSymbolRef &) const; |
| }; |
| |
| struct SymbolOffsetCompare { |
| bool operator()(const SymbolRef &, const SymbolRef &) const; |
| bool operator()(const MutableSymbolRef &, const MutableSymbolRef &) const; |
| }; |
| |
| using UnorderedSymbolSet = std::set<SymbolRef, SymbolAddressCompare>; |
| using SourceOrderedSymbolSet = std::set<SymbolRef, SymbolSourcePositionCompare>; |
| |
| template <typename A> |
| SourceOrderedSymbolSet OrderBySourcePosition(const A &container) { |
| SourceOrderedSymbolSet result; |
| for (SymbolRef x : container) { |
| result.emplace(x); |
| } |
| return result; |
| } |
| |
| } // namespace Fortran::semantics |
| |
| // Define required info so that SymbolRef can be used inside llvm::DenseMap. |
| namespace llvm { |
| template <> struct DenseMapInfo<Fortran::semantics::SymbolRef> { |
| static inline Fortran::semantics::SymbolRef getEmptyKey() { |
| auto ptr = DenseMapInfo<const Fortran::semantics::Symbol *>::getEmptyKey(); |
| return *reinterpret_cast<Fortran::semantics::SymbolRef *>(&ptr); |
| } |
| |
| static inline Fortran::semantics::SymbolRef getTombstoneKey() { |
| auto ptr = |
| DenseMapInfo<const Fortran::semantics::Symbol *>::getTombstoneKey(); |
| return *reinterpret_cast<Fortran::semantics::SymbolRef *>(&ptr); |
| } |
| |
| static unsigned getHashValue(const Fortran::semantics::SymbolRef &sym) { |
| return DenseMapInfo<const Fortran::semantics::Symbol *>::getHashValue( |
| &sym.get()); |
| } |
| |
| static bool isEqual(const Fortran::semantics::SymbolRef &LHS, |
| const Fortran::semantics::SymbolRef &RHS) { |
| return LHS == RHS; |
| } |
| }; |
| } // namespace llvm |
| #endif // FORTRAN_SEMANTICS_SYMBOL_H_ |