llvm/flang/lib/semantics/symbol.h
peter klausler 5a18e79d5a [flang] Remove OwningPointer and ForwardReference
Use std::unique_ptr<> with custom deleter for forward-referenced owned pointer.

Move CopyableIndirection into common, add documentation, clean up.

Remove OwningPointer and ForwardReference

Use std::unique_ptr<> with custom deleter for forward-referenced owned pointer.

Use CopyableIndirection

clean up from merge after split

Complete characterization

fold conversions of arrays

Clean up subscripts to constant arrays

Elemental unary operations complete

Support assumed type TYPE(*) in actual arguments

clean up some TODOs

recognize TYPE(*) arguments to intrinsics

Complete folding of array operations

Finish elementwise array folding, add test, debug

characterize intrinsics, fix some bugs

Clean up build

Type compatibility and shape conformance checks on pointer assignments

Original-commit: flang-compiler/f18@99d734c621
Reviewed-on: https://github.com/flang-compiler/f18/pull/442
Tree-same-pre-rewrite: false
2019-05-06 07:51:07 -07:00

670 lines
23 KiB
C++

// 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.
#ifndef FORTRAN_SEMANTICS_SYMBOL_H_
#define FORTRAN_SEMANTICS_SYMBOL_H_
#include "type.h"
#include "../common/Fortran.h"
#include "../common/enum-set.h"
#include <functional>
#include <list>
#include <optional>
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 SemanticsContext;
class Symbol;
using SymbolList = std::list<const Symbol *>;
// 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 SubprogramDetails {
public:
SubprogramDetails() {}
SubprogramDetails(const SubprogramDetails &that)
: dummyArgs_{that.dummyArgs_}, result_{that.result_} {}
bool isFunction() const { return result_ != nullptr; }
bool isInterface() const { return isInterface_; }
void set_isInterface(bool value = true) { isInterface_ = value; }
MaybeExpr bindName() const { return bindName_; }
void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
const Symbol &result() const {
CHECK(isFunction());
return *result_;
}
void set_result(Symbol &result) {
CHECK(result_ == nullptr);
result_ = &result;
}
const std::list<Symbol *> &dummyArgs() const { return dummyArgs_; }
void add_dummyArg(Symbol &symbol) { dummyArgs_.push_back(&symbol); }
private:
bool isInterface_{false}; // true if this represents an interface-body
MaybeExpr bindName_;
std::list<Symbol *> dummyArgs_;
Symbol *result_{nullptr};
friend std::ostream &operator<<(std::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) : kind_{kind} {}
SubprogramNameDetails() = delete;
SubprogramKind kind() const { return kind_; }
private:
SubprogramKind kind_;
};
// A name from an entity-decl -- could be object or function.
class EntityDetails {
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_; }
bool isFuncResult() const { return isFuncResult_; }
void set_funcResult(bool x) { isFuncResult_ = x; }
MaybeExpr bindName() const { return bindName_; }
void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
private:
bool isDummy_;
bool isFuncResult_{false};
const DeclTypeSpec *type_{nullptr};
MaybeExpr bindName_;
friend std::ostream &operator<<(std::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_; }
private:
MaybeExpr expr_;
};
// 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() && IsArray() && shape_.back().ubound().isDeferred() &&
!shape_.back().lbound().isDeferred();
}
bool IsDeferredShape() const {
return !isDummy() && IsArray() && shape_.back().ubound().isDeferred() &&
shape_.back().lbound().isDeferred();
}
bool IsAssumedSize() const {
return isDummy() && IsArray() && shape_.back().ubound().isAssumed() &&
!shape_.back().lbound().isAssumed();
}
bool IsAssumedRank() const {
return isDummy() && IsArray() && shape_.back().ubound().isAssumed() &&
shape_.back().lbound().isAssumed();
}
private:
MaybeExpr init_;
ArraySpec shape_;
ArraySpec coshape_;
const Symbol *commonBlock_{nullptr}; // common block this object is in
friend std::ostream &operator<<(std::ostream &, const ObjectEntityDetails &);
};
// A procedure pointer, dummy procedure, or external procedure
class ProcEntityDetails : public EntityDetails {
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; }
const std::optional<SourceName> &passName() const { return passName_; }
void set_passName(const SourceName &passName) { passName_ = passName; }
inline bool HasExplicitInterface() const;
private:
ProcInterface interface_;
std::optional<SourceName> passName_;
friend std::ostream &operator<<(std::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> &paramNames() const { return paramNames_; }
const SymbolList &paramDecls() const { return paramDecls_; }
bool sequence() const { return sequence_; }
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; }
// Returns the complete list of derived type parameter names in the
// order defined by 7.5.3.2.
std::list<SourceName> OrderParameterNames(const Symbol &) const;
// Returns the complete list of derived type parameter symbols in
// the order in which their declarations appear in the derived type
// definitions (parents first).
SymbolList OrderParameterDeclarations(const Symbol &) const;
// Returns the complete list of derived type components in the order
// in which their declarations appear in the derived type definitions
// (parents first). Parent components appear in the list immediately
// after the components that belong to them.
SymbolList OrderComponents(const Scope &) const;
// 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();
}
}
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_;
SymbolList 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_;
bool sequence_{false};
friend std::ostream &operator<<(std::ostream &, const DerivedTypeDetails &);
};
class ProcBindingDetails {
public:
explicit ProcBindingDetails(const Symbol &symbol) : symbol_{&symbol} {}
const Symbol &symbol() const { return *symbol_; }
std::optional<SourceName> passName() const { return passName_; }
void set_passName(const SourceName &passName) { passName_ = passName; }
private:
const Symbol *symbol_; // procedure bound to
std::optional<SourceName> passName_; // name in PASS attribute
};
ENUM_CLASS(GenericKind, // Kinds of generic-spec
Name, DefinedOp, // these have a Name associated with them
Assignment, // user-defined assignment
OpPower, OpMultiply, OpDivide, OpAdd, OpSubtract, OpConcat, OpLT, OpLE,
OpEQ, OpNE, OpGE, OpGT, OpNOT, OpAND, OpOR, OpXOR, OpEQV, OpNEQV,
ReadFormatted, ReadUnformatted, WriteFormatted, WriteUnformatted)
class GenericBindingDetails {
public:
GenericBindingDetails() {}
GenericKind kind() const { return kind_; }
void set_kind(GenericKind kind) { kind_ = kind; }
const SymbolList &specificProcs() const { return specificProcs_; }
void add_specificProc(const Symbol &proc) { specificProcs_.push_back(&proc); }
void add_specificProcs(const SymbolList &procs) {
specificProcs_.insert(specificProcs_.end(), procs.begin(), procs.end());
}
private:
GenericKind kind_{GenericKind::Name};
SymbolList specificProcs_;
};
class NamelistDetails {
public:
const SymbolList &objects() const { return objects_; }
void add_object(const Symbol &object) { objects_.push_back(&object); }
void add_objects(const SymbolList &objects) {
objects_.insert(objects_.end(), objects.begin(), objects.end());
}
private:
SymbolList objects_;
};
class CommonBlockDetails {
public:
std::list<Symbol *> &objects() { return objects_; }
const std::list<Symbol *> &objects() const { return objects_; }
void add_object(Symbol &object) { objects_.push_back(&object); }
MaybeExpr bindName() const { return bindName_; }
void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
private:
std::list<Symbol *> objects_;
MaybeExpr bindName_;
};
class FinalProcDetails {};
class MiscDetails {
public:
ENUM_CLASS(Kind, None, ConstructName, ScopeName, PassName, ComplexPartRe,
ComplexPartIm, KindParamInquiry, LenParamInquiry, SelectTypeAssociateName,
SpecificIntrinsic);
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 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_; }
const Symbol &module() const;
private:
SourceName location_;
const Symbol *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_; }
private:
const Symbol *symbol_;
};
class GenericDetails {
public:
GenericDetails() {}
GenericDetails(const SymbolList &specificProcs);
GenericDetails(Symbol *specific) : specific_{specific} {}
GenericKind kind() const { return kind_; }
void set_kind(GenericKind kind) { kind_ = kind; }
const SymbolList specificProcs() const { return specificProcs_; }
void add_specificProc(const Symbol &proc) { specificProcs_.push_back(&proc); }
Symbol *specific() { return specific_; }
const Symbol *specific() const { return specific_; }
void set_specific(Symbol &specific);
// Derived type with same name as generic, if any.
Symbol *derivedType() { return derivedType_; }
const Symbol *derivedType() const { return derivedType_; }
void set_derivedType(Symbol &derivedType);
// 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_{GenericKind::Name};
// all of the specific procedures for this generic
SymbolList specificProcs_;
// 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};
};
class UnknownDetails {};
using Details = std::variant<UnknownDetails, MainProgramDetails, ModuleDetails,
SubprogramDetails, SubprogramNameDetails, EntityDetails,
ObjectEntityDetails, ProcEntityDetails, AssocEntityDetails,
DerivedTypeDetails, UseDetails, UseErrorDetails, HostAssocDetails,
GenericDetails, ProcBindingDetails, GenericBindingDetails, NamelistDetails,
CommonBlockDetails, FinalProcDetails, TypeParamDetails, MiscDetails>;
std::ostream &operator<<(std::ostream &, const Details &);
std::string DetailsToString(const Details &);
class Symbol {
public:
ENUM_CLASS(Flag,
Error, // an error has been reported on this symbol
Function, // symbol is a function
Subroutine, // symbol is a subroutine
Implicit, // symbol is implicitly typed
ModFile, // symbol came from .mod file
ParentComp, // symbol is the "parent component" of an extended type
LocalityLocal, // named in LOCAL locality-spec
LocalityLocalInit, // named in LOCAL_INIT locality-spec
LocalityShared // named in SHARED locality-spec
);
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; }
// 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 != nullptr);
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 to get the ultimate entity.
Symbol &GetUltimate();
const Symbol &GetUltimate() const;
DeclTypeSpec *GetType() {
return const_cast<DeclTypeSpec *>(
const_cast<const Symbol *>(this)->GetType());
}
const DeclTypeSpec *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) {
if (const Symbol * symbol{x.interface().symbol()}) {
return symbol->GetType();
} else {
return x.interface().type();
}
},
[](const TypeParamDetails &x) { return x.type(); },
[](const UseDetails &x) { return x.symbol().GetType(); },
[](const auto &) -> const DeclTypeSpec * { return nullptr; },
},
details_);
}
void SetType(const DeclTypeSpec &);
bool IsDummy() const;
bool IsFuncResult() const;
bool IsObjectArray() const;
bool IsSubprogram() const;
bool IsSeparateModuleProc() const;
bool HasExplicitInterface() const {
return std::visit(
common::visitors{
[](const SubprogramDetails &) { return true; },
[](const SubprogramNameDetails &) { return true; },
[](const ProcEntityDetails &x) { return x.HasExplicitInterface(); },
[](const UseDetails &x) {
return x.symbol().HasExplicitInterface();
},
[](const MiscDetails &x) {
return x.kind() == MiscDetails::Kind::SpecificIntrinsic;
},
[](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 UseDetails &x) { return x.symbol().Rank(); },
[](const HostAssocDetails &x) { return x.symbol().Rank(); },
[](const ObjectEntityDetails &oed) {
return static_cast<int>(oed.shape().size());
},
[](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 static_cast<int>(oed.coshape().size());
},
[](const auto &) { return 0; },
},
details_);
}
// Clones the Symbol in the context of a parameterized derived type instance
Symbol &Instantiate(Scope &, SemanticsContext &) const;
// 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;
private:
const Scope *owner_;
SourceName name_;
Attrs attrs_;
Flags flags_;
Scope *scope_{nullptr};
Details details_;
Symbol() {} // only created in class Symbols
const std::string GetDetailsName() const;
friend std::ostream &operator<<(std::ostream &, const Symbol &);
friend std::ostream &DumpForUnparse(std::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;
};
std::ostream &operator<<(std::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;
}
}
#endif // FORTRAN_SEMANTICS_SYMBOL_H_