//===-- include/flang/Semantics/scope.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_SCOPE_H_
#define FORTRAN_SEMANTICS_SCOPE_H_
#include "attr.h"
#include "symbol.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/idioms.h"
#include "flang/Common/reference.h"
#include "flang/Parser/message.h"
#include "flang/Parser/provenance.h"
#include <list>
#include <map>
#include <optional>
#include <set>
#include <string>
namespace llvm {
class raw_ostream;
}
namespace Fortran::semantics {
using namespace parser::literals;
using common::ConstantSubscript;
class SemanticsContext;
// An equivalence object is represented by a symbol for the variable name,
// the indices for an array element, and the lower bound for a substring.
struct EquivalenceObject {
EquivalenceObject(Symbol &symbol, std::vector<ConstantSubscript> subscripts,
std::optional<ConstantSubscript> substringStart, parser::CharBlock source)
: symbol{symbol}, subscripts{subscripts},
substringStart{substringStart}, source{source} {}
explicit EquivalenceObject(Symbol &symbol)
: symbol{symbol}, source{symbol.name()} {}
bool operator==(const EquivalenceObject &) const;
bool operator<(const EquivalenceObject &) const;
std::string AsFortran() const;
Symbol &symbol;
std::vector<ConstantSubscript> subscripts; // for array elem
std::optional<ConstantSubscript> substringStart;
parser::CharBlock source;
};
using EquivalenceSet = std::vector<EquivalenceObject>;
class Scope {
using mapType = std::map<SourceName, MutableSymbolRef>;
public:
ENUM_CLASS(Kind, Global, IntrinsicModules, Module, MainProgram, Subprogram,
BlockData, DerivedType, BlockConstruct, Forall, OtherConstruct,
OpenACCConstruct, ImpliedDos)
using ImportKind = common::ImportKind;
// Create the Global scope -- the root of the scope tree
explicit Scope(SemanticsContext &context)
: Scope{*this, Kind::Global, nullptr, context} {}
Scope(Scope &parent, Kind kind, Symbol *symbol, SemanticsContext &context)
: parent_{&parent}, kind_{kind}, symbol_{symbol}, context_{context} {
if (symbol) {
symbol->set_scope(this);
}
}
Scope(const Scope &) = delete;
bool operator==(const Scope &that) const { return this == &that; }
bool operator!=(const Scope &that) const { return this != &that; }
Scope &parent() {
CHECK(parent_ != this);
return *parent_;
}
const Scope &parent() const {
CHECK(parent_ != this);
return *parent_;
}
Kind kind() const { return kind_; }
bool IsGlobal() const { return kind_ == Kind::Global; }
bool IsIntrinsicModules() const { return kind_ == Kind::IntrinsicModules; }
bool IsTopLevel() const {
return kind_ == Kind::Global || kind_ == Kind::IntrinsicModules;
}
bool IsModule() const {
return kind_ == Kind::Module &&
!symbol_->get<ModuleDetails>().isSubmodule();
}
bool IsSubmodule() const {
return kind_ == Kind::Module && symbol_->get<ModuleDetails>().isSubmodule();
}
bool IsDerivedType() const { return kind_ == Kind::DerivedType; }
bool IsStmtFunction() const;
bool IsParameterizedDerivedType() const;
bool IsParameterizedDerivedTypeInstantiation() const {
return kind_ == Kind::DerivedType && !symbol_;
}
/// Does this derived type have at least one kind parameter ?
bool IsDerivedTypeWithKindParameter() const;
/// Does this derived type have at least one length parameter ?
bool IsDerivedTypeWithLengthParameter() const;
Symbol *symbol() { return symbol_; }
const Symbol *symbol() const { return symbol_; }
SemanticsContext &context() const { return context_; }
inline const Symbol *GetSymbol() const;
const Scope *GetDerivedTypeParent() const;
const Scope &GetDerivedTypeBase() const;
inline std::optional<SourceName> GetName() const;
// Returns true if this scope contains, or is, another scope.
bool Contains(const Scope &) const;
/// Make a scope nested in this one
Scope &MakeScope(Kind kind, Symbol *symbol = nullptr);
SemanticsContext &GetMutableSemanticsContext() const {
return const_cast<SemanticsContext &>(context());
}
using size_type = mapType::size_type;
using iterator = mapType::iterator;
using const_iterator = mapType::const_iterator;
iterator begin() { return symbols_.begin(); }
iterator end() { return symbols_.end(); }
const_iterator begin() const { return symbols_.begin(); }
const_iterator end() const { return symbols_.end(); }
const_iterator cbegin() const { return symbols_.cbegin(); }
const_iterator cend() const { return symbols_.cend(); }
// Return symbols in declaration order (the iterators above are in name order)
SymbolVector GetSymbols() const;
MutableSymbolVector GetSymbols();
iterator find(const SourceName &name);
const_iterator find(const SourceName &name) const {
return symbols_.find(name);
}
size_type erase(const SourceName &);
bool empty() const { return symbols_.empty(); }
// Look for symbol by name in this scope and host (depending on imports).
Symbol *FindSymbol(const SourceName &) const;
// Look for component symbol by name in a derived type's scope and
// parents'.
Symbol *FindComponent(SourceName) const;
/// Make a Symbol with unknown details.
std::pair<iterator, bool> try_emplace(
const SourceName &name, Attrs attrs = Attrs()) {
return try_emplace(name, attrs, UnknownDetails());
}
/// Make a Symbol with provided details.
template <typename D>
common::IfNoLvalue<std::pair<iterator, bool>, D> try_emplace(
const SourceName &name, D &&details) {
return try_emplace(name, Attrs(), std::move(details));
}
/// Make a Symbol with attrs and details
template <typename D>
common::IfNoLvalue<std::pair<iterator, bool>, D> try_emplace(
const SourceName &name, Attrs attrs, D &&details) {
Symbol &symbol{MakeSymbol(name, attrs, std::move(details))};
return symbols_.emplace(name, symbol);
}
// Make a copy of a symbol in this scope; nullptr if one is already there
Symbol *CopySymbol(const Symbol &);
std::list<EquivalenceSet> &equivalenceSets() { return equivalenceSets_; }
const std::list<EquivalenceSet> &equivalenceSets() const {
return equivalenceSets_;
}
void add_equivalenceSet(EquivalenceSet &&);
// Cray pointers are saved as map of pointee name -> pointer symbol
const mapType &crayPointers() const { return crayPointers_; }
void add_crayPointer(const SourceName &, Symbol &);
mapType &commonBlocks() { return commonBlocks_; }
const mapType &commonBlocks() const { return commonBlocks_; }
Symbol &MakeCommonBlock(const SourceName &);
Symbol *FindCommonBlock(const SourceName &) const;
/// Make a Symbol but don't add it to the scope.
template <typename D>
common::IfNoLvalue<Symbol &, D> MakeSymbol(
const SourceName &name, Attrs attrs, D &&details) {
return allSymbols.Make(*this, name, attrs, std::move(details));
}
std::list<Scope> &children() { return children_; }
const std::list<Scope> &children() const { return children_; }
// For Module scope, maintain a mapping of all submodule scopes with this
// module as its ancestor module. AddSubmodule returns false if already there.
Scope *FindSubmodule(const SourceName &) const;
bool AddSubmodule(const SourceName &, Scope &);
const DeclTypeSpec *FindType(const DeclTypeSpec &) const;
const DeclTypeSpec &MakeNumericType(TypeCategory, KindExpr &&kind);
const DeclTypeSpec &MakeLogicalType(KindExpr &&kind);
const DeclTypeSpec &MakeCharacterType(
ParamValue &&length, KindExpr &&kind = KindExpr{0});
DeclTypeSpec &MakeDerivedType(DeclTypeSpec::Category, DerivedTypeSpec &&);
const DeclTypeSpec &MakeTypeStarType();
const DeclTypeSpec &MakeClassStarType();
const DeclTypeSpec *GetType(const SomeExpr &);
std::size_t size() const { return size_; }
void set_size(std::size_t size) { size_ = size; }
std::optional<std::size_t> alignment() const { return alignment_; }
void SetAlignment(std::size_t n) {
alignment_ = std::max(alignment_.value_or(0), n);
}
ImportKind GetImportKind() const;
// Names appearing in IMPORT statements in this scope
std::set<SourceName> importNames() const { return importNames_; }
bool CanImport(const SourceName &) const;
// Set the kind of imports from host into this scope.
// Return an error message for incompatible kinds.
std::optional<parser::MessageFixedText> SetImportKind(ImportKind);
void add_importName(const SourceName &);
// These members pertain to instantiations of parameterized derived types.
const DerivedTypeSpec *derivedTypeSpec() const { return derivedTypeSpec_; }
DerivedTypeSpec *derivedTypeSpec() { return derivedTypeSpec_; }
void set_derivedTypeSpec(DerivedTypeSpec &spec) { derivedTypeSpec_ = &spec; }
parser::Message::Reference instantiationContext() const {
return instantiationContext_;
};
void set_instantiationContext(parser::Message::Reference &&mref) {
instantiationContext_ = std::move(mref);
}
bool hasSAVE() const { return hasSAVE_; }
void set_hasSAVE(bool yes = true) { hasSAVE_ = yes; }
// The range of the source of this and nested scopes.
const parser::CharBlock &sourceRange() const { return sourceRange_; }
void AddSourceRange(parser::CharBlock);
// Attempts to find a match for a derived type instance
const DeclTypeSpec *FindInstantiatedDerivedType(const DerivedTypeSpec &,
DeclTypeSpec::Category = DeclTypeSpec::TypeDerived) const;
bool IsModuleFile() const {
return kind_ == Kind::Module && symbol_ &&
symbol_->test(Symbol::Flag::ModFile);
}
void InstantiateDerivedTypes();
const Symbol *runtimeDerivedTypeDescription() const {
return runtimeDerivedTypeDescription_;
}
void set_runtimeDerivedTypeDescription(const Symbol &symbol) {
runtimeDerivedTypeDescription_ = &symbol;
}
private:
Scope *parent_{
nullptr}; // this is enclosing scope, not extended derived type base
const Kind kind_;
std::size_t size_{0}; // size in bytes
std::optional<std::size_t> alignment_; // required alignment in bytes
parser::CharBlock sourceRange_;
const parser::CookedSource *cookedSource_{nullptr};
Symbol *const symbol_; // if not null, symbol_->scope() == this
std::list<Scope> children_;
mapType symbols_;
mapType commonBlocks_;
std::list<EquivalenceSet> equivalenceSets_;
mapType crayPointers_;
std::map<SourceName, common::Reference<Scope>> submodules_;
std::list<DeclTypeSpec> declTypeSpecs_;
std::optional<ImportKind> importKind_;
std::set<SourceName> importNames_;
DerivedTypeSpec *derivedTypeSpec_{nullptr}; // dTS->scope() == this
parser::Message::Reference instantiationContext_;
bool hasSAVE_{false}; // scope has a bare SAVE statement
const Symbol *runtimeDerivedTypeDescription_{nullptr};
SemanticsContext &context_;
// When additional data members are added to Scope, remember to
// copy them, if appropriate, in FindOrInstantiateDerivedType().
// Storage for all Symbols. Every Symbol is in allSymbols and every Symbol*
// or Symbol& points to one in there.
static Symbols<1024> allSymbols;
const DeclTypeSpec &MakeLengthlessType(DeclTypeSpec &&);
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Scope &);
};
// Inline so that it can be called from Evaluate without a link-time dependency.
inline const Symbol *Scope::GetSymbol() const {
return symbol_ ? symbol_
: derivedTypeSpec_ ? &derivedTypeSpec_->typeSymbol()
: nullptr;
}
inline std::optional<SourceName> Scope::GetName() const {
if (const auto *sym{GetSymbol()}) {
return sym->name();
} else {
return std::nullopt;
}
}
} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_SCOPE_H_