From: Tim Keith Date: Wed, 7 Feb 2018 00:46:29 +0000 (-0800) Subject: [flang] Initial work on the representation of types. X-Git-Tag: llvmorg-12-init~9537^2~2962 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=59157ff1a5dc637d9d009379200d299536690fe9;p=platform%2Fupstream%2Fllvm.git [flang] Initial work on the representation of types. Still work to do for derived types components, array specs, type-bound procedures, etc. Added executable type-test to the cmake file which exercises some of the basic functionality. Modified the Makefile so that "make Debug" does a cmake build. Original-commit: flang-compiler/f18@02e8c4c867685959f8623ca8a4c2277756ecbe6c Reviewed-on: https://github.com/flang-compiler/f18/pull/3 Tree-same-pre-rewrite: false --- diff --git a/flang/CMakeLists.txt b/flang/CMakeLists.txt index 1d59b87..08bd5f6 100644 --- a/flang/CMakeLists.txt +++ b/flang/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.9.0) -project(f18) set(GCC /home/sw/thirdparty/gcc/gcc-7.3.0/linux86-64/redhat) set(CMAKE_CXX_COMPILER "${GCC}/bin/g++") set(CMAKE_INSTALL_RPATH "${GCC}/lib64") set(CMAKE_BUILD_WITH_INSTALL_RPATH true) +project(f18) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17") set(SOURCES @@ -20,3 +20,4 @@ set(SOURCES lib/parser/source.cc ) add_executable(f18 ${SOURCES}) +add_executable(type-test type.cc idioms.cc attr.cc) diff --git a/flang/Makefile b/flang/Makefile index 5d0d242..c575021 100644 --- a/flang/Makefile +++ b/flang/Makefile @@ -61,3 +61,8 @@ formatted: done .PHONY: formatted + +Debug Release: + @mkdir -p $@ + cd $@ && cmake -DCMAKE_BUILD_TYPE=$@ .. && make +.PHONY: Debug Release diff --git a/flang/attr.cc b/flang/attr.cc new file mode 100644 index 0000000..cc9564c --- /dev/null +++ b/flang/attr.cc @@ -0,0 +1,52 @@ +#include "attr.h" +#include +#include + +namespace Fortran { + +std::ostream &operator<<(std::ostream &o, Attr attr) { + switch (attr) { + case Attr::ABSTRACT: return o << "ABSTRACT"; + case Attr::ALLOCATABLE: return o << "ALLOCATABLE"; + case Attr::ASYNCHRONOUS: return o << "ASYNCHRONOUS"; + case Attr::BIND_C: return o << "BIND(C)"; + case Attr::CONTIGUOUS: return o << "CONTIGUOUS"; + case Attr::EXTERNAL: return o << "EXTERNAL"; + case Attr::INTENT_IN: return o << "INTENT_IN"; + case Attr::INTENT_OUT: return o << "INTENT_OUT"; + case Attr::INTRINSIC: return o << "INTRINSIC"; + case Attr::OPTIONAL: return o << "OPTIONAL"; + case Attr::PARAMETER: return o << "PARAMETER"; + case Attr::POINTER: return o << "POINTER"; + case Attr::PRIVATE: return o << "PRIVATE"; + case Attr::PROTECTED: return o << "PROTECTED"; + case Attr::PUBLIC: return o << "PUBLIC"; + case Attr::SAVE: return o << "SAVE"; + case Attr::TARGET: return o << "TARGET"; + case Attr::VALUE: return o << "VALUE"; + case Attr::VOLATILE: return o << "VOLATILE"; + default: CRASH_NO_CASE; + } +} + +std::ostream &operator<<(std::ostream &o, const Attrs &attrs) { + int n = 0; + for (auto attr : attrs) { + if (n++) o << ", "; + o << attr; + } + return o; +} + +void checkAttrs(std::string className, Attrs attrs, Attrs allowed) { + for (auto attr : attrs) { + if (allowed.find(attr) == allowed.end()) { + std::stringstream temp; + temp << attr; + die("invalid attribute '%s' for class %s", temp.str().c_str(), + className.c_str()); + } + } +} + +} // namespace Fortran diff --git a/flang/attr.h b/flang/attr.h new file mode 100644 index 0000000..e1d7aca --- /dev/null +++ b/flang/attr.h @@ -0,0 +1,45 @@ +#ifndef FORTRAN_ATTR_H_ +#define FORTRAN_ATTR_H_ + +#include +#include +#include + +#include "idioms.h" + +namespace Fortran { + +// All available attributes. +enum class Attr { + ABSTRACT, + ALLOCATABLE, + ASYNCHRONOUS, + BIND_C, + CONTIGUOUS, + EXTERNAL, + INTENT_IN, + INTENT_OUT, + INTRINSIC, + OPTIONAL, + PARAMETER, + POINTER, + PRIVATE, + PROTECTED, + PUBLIC, + SAVE, + TARGET, + VALUE, + VOLATILE, +}; + +using Attrs = std::set; + +std::ostream &operator<<(std::ostream &o, Attr attr); +std::ostream &operator<<(std::ostream &o, const Attrs &attrs); + +// Report internal error if attrs is not a subset of allowed. +void checkAttrs(std::string className, Attrs attrs, Attrs allowed); + +} // namespace Fortran + +#endif diff --git a/flang/type.cc b/flang/type.cc new file mode 100644 index 0000000..6904be5 --- /dev/null +++ b/flang/type.cc @@ -0,0 +1,250 @@ +#include + +#include "type.h" + +namespace Fortran { + +// Check that values specified for param defs are valid: they must match the +// names of the params and any def that doesn't have a default value must have a +// value. +template +static void checkParams( + std::string kindOrLen, TypeParamDefs defs, std::map values) { + std::set validNames{}; + for (TypeParamDef def : defs) { + Name name = def.name(); + validNames.insert(name); + if (!def.defaultValue() && values.find(name) == values.end()) { + die("no value or default value for %s parameter '%s'", kindOrLen.c_str(), + name.c_str()); + } + } + for (auto pair : values) { + Name name = pair.first; + if (validNames.find(name) == validNames.end()) { + die("invalid %s parameter '%s'", kindOrLen.c_str(), name.c_str()); + } + } +} + +const IntConst IntConst::ZERO = IntConst{0}; +const IntConst IntConst::ONE = IntConst{1}; + +const IntExpr *IntConst::clone() const { + if (*this == ZERO) { + return &ZERO; + } else if (*this == ONE) { + return &ONE; + } else { + return new IntConst{*this}; + } +} + +std::ostream &operator<<(std::ostream &o, const KindParamValue &x) { + return o << x.value_; +} + +const LenParamValue LenParamValue::ASSUMED = + LenParamValue(LenParamValue::Assumed); +const LenParamValue LenParamValue::DEFERRED = + LenParamValue(LenParamValue::Deferred); + +std::ostream &operator<<(std::ostream &o, const LenParamValue &x) { + switch (x.category_) { + case LenParamValue::Assumed: return o << '*'; + case LenParamValue::Deferred: return o << ':'; + case LenParamValue::Expr: return o << *x.value_; + default: CRASH_NO_CASE; + } +} + +KindedTypeHelper LogicalTypeSpec::helper{"LOGICAL", 0}; +std::ostream &operator<<(std::ostream &o, const LogicalTypeSpec &x) { + return LogicalTypeSpec::helper.output(o, x); +} + +KindedTypeHelper IntegerTypeSpec::helper{"INTEGER", 0}; +std::ostream &operator<<(std::ostream &o, const IntegerTypeSpec &x) { + return IntegerTypeSpec::helper.output(o, x); +} + +KindedTypeHelper RealTypeSpec::helper{"REAL", 0}; +std::ostream &operator<<(std::ostream &o, const RealTypeSpec &x) { + return RealTypeSpec::helper.output(o, x); +} + +KindedTypeHelper ComplexTypeSpec::helper{"COMPLEX", 0}; +std::ostream &operator<<(std::ostream &o, const ComplexTypeSpec &x) { + return ComplexTypeSpec::helper.output(o, x); +} + +std::ostream &operator<<(std::ostream &o, const CharacterTypeSpec &x) { + o << "CHARACTER(" << x.len_; + if (x.kind_ != CharacterTypeSpec::DefaultKind) { + o << ", " << x.kind_; + } + return o << ')'; +} + +DerivedTypeDef::DerivedTypeDef(const Name &name, const Attrs &attrs, + const TypeParamDefs &lenParams, const TypeParamDefs &kindParams, + bool private_, bool sequence) + : name_{name}, attrs_{attrs}, lenParams_{lenParams}, + kindParams_{kindParams}, private_{private_}, sequence_{sequence} { + checkAttrs("DerivedTypeDef", attrs, + Attrs{Attr::ABSTRACT, Attr::PUBLIC, Attr::PRIVATE, Attr::BIND_C}); +} + +std::ostream &operator<<(std::ostream &o, const DerivedTypeDef &x) { + o << "TYPE"; + for (auto attr : x.attrs_) { + o << ", " << attr; + } + o << " :: " << x.name_; + if (x.lenParams_.size() > 0 || x.kindParams_.size() > 0) { + o << '('; + int n = 0; + for (auto param : x.lenParams_) { + if (n++) o << ", "; + o << param.name(); + } + for (auto param : x.kindParams_) { + if (n++) o << ", "; + o << param.name(); + } + o << ')'; + } + o << '\n'; + for (auto param : x.lenParams_) { + o << " " << param.type() << ", LEN :: " << param.name() << "\n"; + } + for (auto param : x.kindParams_) { + o << " " << param.type() << ", KIND :: " << param.name() << "\n"; + } + if (x.private_) o << " PRIVATE\n"; + if (x.sequence_) o << " SEQUENCE\n"; + // components + return o << "END TYPE\n"; +} + +DerivedTypeSpec::DerivedTypeSpec(DerivedTypeDef def, + KindParamValues kindParamValues, LenParamValues lenParamValues) + : def_{def}, kindParamValues_{kindParamValues}, lenParamValues_{ + lenParamValues} { + checkParams("kind", def.kindParams_, kindParamValues); + checkParams("len", def.lenParams_, lenParamValues); +} + +std::ostream &operator<<(std::ostream &o, const DerivedTypeSpec &x) { + o << "TYPE(" << x.def_.name_; + if (x.kindParamValues_.size() > 0 || x.lenParamValues_.size() > 0) { + o << '('; + int n = 0; + for (auto pair : x.kindParamValues_) { + if (n++) o << ", "; + o << pair.first << '=' << pair.second; + } + for (auto pair : x.lenParamValues_) { + if (n++) o << ", "; + o << pair.first << '=' << pair.second; + } + o << ')'; + } + o << ')'; + return o; +} + +const Bound Bound::ASSUMED{Bound::Assumed}; +const Bound Bound::DEFERRED{Bound::Deferred}; + +std::ostream &operator<<(std::ostream &o, const Bound &x) { + if (x.isAssumed()) { + o << '*'; + } else if (x.isDeferred()) { + o << ':'; + } else { + x.expr_->output(o); + } + return o; +} + +std::ostream &operator<<(std::ostream &o, const ShapeSpec &x) { + if (x.lb_.isAssumed()) { + CHECK(x.ub_.isAssumed()); + o << ".."; + } else { + if (!x.lb_.isDeferred()) o << x.lb_; + o << ':'; + if (!x.ub_.isDeferred()) o << x.ub_; + } + return o; +} + +} // namespace Fortran + +using namespace Fortran; + +void testTypeSpec() { + LogicalTypeSpec l1 = LogicalTypeSpec::make(); + LogicalTypeSpec l2 = LogicalTypeSpec::make(2); + std::cout << l1 << "\n"; + std::cout << l2 << "\n"; + RealTypeSpec r1 = RealTypeSpec::make(); + RealTypeSpec r2 = RealTypeSpec::make(2); + std::cout << r1 << "\n"; + std::cout << r2 << "\n"; + CharacterTypeSpec c1{LenParamValue::DEFERRED, 1}; + std::cout << c1 << "\n"; + CharacterTypeSpec c2{IntConst{10}}; + std::cout << c2 << "\n"; + + IntegerTypeSpec i1 = IntegerTypeSpec::make(); + IntegerTypeSpec i2 = IntegerTypeSpec::make(2); + TypeParamDef lenParam{"my_len", i2}; + TypeParamDef kindParam{"my_kind", i1}; + + DerivedTypeDef def1{ + "my_name", + {Attr::PRIVATE, Attr::BIND_C}, + TypeParamDefs{lenParam}, + TypeParamDefs{kindParam}, + sequence : true + }; + + LenParamValues lenParamValues{ + LenParamValues::value_type{"my_len", LenParamValue::ASSUMED}, + }; + KindParamValues kindParamValues{ + KindParamValues::value_type{"my_kind", KindParamValue{123}}, + }; + DerivedTypeSpec dt1{def1, kindParamValues, lenParamValues}; + std::cout << dt1 << "\n"; +} + +void testShapeSpec() { + IntConst ten{10}; + const ShapeSpec s1{ShapeSpec::makeExplicit(ten)}; + std::cout << "explicit-shape-spec: " << s1 << "\n"; + ShapeSpec s2{ShapeSpec::makeExplicit(IntConst{2}, IntConst{8})}; + std::cout << "explicit-shape-spec: " << s2 << "\n"; + + ShapeSpec s3{ShapeSpec::makeAssumed()}; + std::cout << "assumed-shape-spec: " << s3 << "\n"; + ShapeSpec s4{ShapeSpec::makeAssumed(IntConst{2})}; + std::cout << "assumed-shape-spec: " << s4 << "\n"; + + ShapeSpec s5{ShapeSpec::makeDeferred()}; + std::cout << "deferred-shape-spec: " << s5 << "\n"; + + ShapeSpec s6{ShapeSpec::makeImplied(IntConst{2})}; + std::cout << "implied-shape-spec: " << s6 << "\n"; + + ShapeSpec s7{ShapeSpec::makeAssumedRank()}; + std::cout << "assumed-rank-spec: " << s7 << "\n"; +} + +int main() { + testTypeSpec(); + testShapeSpec(); + return 0; +} diff --git a/flang/type.h b/flang/type.h new file mode 100644 index 0000000..1553c73 --- /dev/null +++ b/flang/type.h @@ -0,0 +1,392 @@ +#ifndef FORTRAN_TYPE_H_ +#define FORTRAN_TYPE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "attr.h" +#include "idioms.h" + +/* + +Type specs are represented by a class hierarchy rooted at TypeSpec. Only the +leaves are concrete types: + TypeSpec + IntrinsicTypeSpec + CharacterTypeSpec + LogicalTypeSpec + NumericTypeSpec + IntegerTypeSpec + RealTypeSpec + ComplexTypeSpec + DerivedTypeSpec + +TypeSpec classes are immutable. For instrinsic types (except character) there +is a limited number of instances -- one for each kind. + +A DerivedTypeSpec is based on a DerivedTypeDef (from a derived type statement) +with kind and len parameter values provided. + +Attributes: +The enum class Attr contains all possible attributes. DerivedTypeDef checks +that supplied attributes are among the allowed ones using checkAttrs(). + +*/ + +namespace Fortran { + +using Name = std::string; + +// TODO +class IntExpr { +public: + virtual const IntExpr *clone() const { return new IntExpr{*this}; } + virtual std::ostream &output(std::ostream &o) const { return o << "IntExpr"; } +}; +std::ostream &operator<<(std::ostream &o, const IntExpr &x) { + return x.output(o); +} + +// TODO +class IntConst : public IntExpr { +public: + static const IntConst ZERO; + static const IntConst ONE; + IntConst(int value) : value_{value} {} + virtual const IntExpr *clone() const; + bool operator==(const IntConst &x) const { return value_ == x.value_; } + bool operator!=(const IntConst &x) const { return !operator==(x); } + bool operator<(const IntConst &x) const { return value_ < x.value_; } + virtual std::ostream &output(std::ostream &o) const { + return o << this->value_; + } + +private: + const int value_; +}; + +// The value of a kind type parameter +class KindParamValue { +public: + KindParamValue(int value) : value_{value} {} + bool operator==(const KindParamValue &x) const { return value_ == x.value_; } + bool operator!=(const KindParamValue &x) const { return !operator==(x); } + bool operator<(const KindParamValue &x) const { return value_ < x.value_; } + +private: + const IntConst value_; + friend std::ostream &operator<<(std::ostream &, const KindParamValue &); +}; + +// The value of a len type parameter +class LenParamValue { +public: + static const LenParamValue ASSUMED; + static const LenParamValue DEFERRED; + LenParamValue(const IntExpr &value) : category_{Expr}, value_{value} {} + +private: + enum Category { Assumed, Deferred, Expr }; + LenParamValue(Category category) : category_{category} {} + const Category category_; + const std::optional value_; + friend std::ostream &operator<<(std::ostream &, const LenParamValue &); +}; + +// Root of the *TypeSpec hierarchy +class TypeSpec { +protected: + TypeSpec() {} + virtual ~TypeSpec() = 0; +}; +TypeSpec::~TypeSpec() {} + +class IntrinsicTypeSpec : public TypeSpec { +public: + const KindParamValue &kind() { return kind_; } + +protected: + IntrinsicTypeSpec(KindParamValue kind) : kind_{kind} {} + virtual ~IntrinsicTypeSpec() = 0; + const KindParamValue kind_; +}; +IntrinsicTypeSpec::~IntrinsicTypeSpec() {} + +class NumericTypeSpec : public IntrinsicTypeSpec { +protected: + NumericTypeSpec(KindParamValue kind) : IntrinsicTypeSpec(kind) {} + virtual ~NumericTypeSpec() = 0; +}; +NumericTypeSpec::~NumericTypeSpec() {} + +namespace { + +// Helper to cache mapping of kind to TypeSpec +template class KindedTypeHelper { +public: + std::map cache; + KindedTypeHelper(Name name, KindParamValue defaultValue) + : name_{name}, defaultValue_{defaultValue} {} + const T &make() { return make(defaultValue_); } + const T &make(KindParamValue kind) { + auto it = cache.find(kind); + if (it == cache.end()) { + it = cache.insert(std::make_pair(kind, T{kind})).first; + } + return it->second; + } + std::ostream &output(std::ostream &o, const T &x) { + o << name_; + if (x.kind_ != defaultValue_) o << '(' << x.kind_ << ')'; + return o; + } + +private: + const Name name_; + const KindParamValue defaultValue_; +}; + +} // namespace + +// One unique instance of LogicalTypeSpec for each kind. +class LogicalTypeSpec : public IntrinsicTypeSpec { +public: + static const LogicalTypeSpec &make() { return helper.make(); } + static const LogicalTypeSpec &make(KindParamValue kind) { + return helper.make(kind); + } + +private: + friend class KindedTypeHelper; + static KindedTypeHelper helper; + LogicalTypeSpec(KindParamValue kind) : IntrinsicTypeSpec(kind) {} + friend std::ostream &operator<<(std::ostream &o, const LogicalTypeSpec &x); +}; + +// One unique instance of IntegerTypeSpec for each kind. +class IntegerTypeSpec : public NumericTypeSpec { +public: + static const IntegerTypeSpec &make() { return helper.make(); } + static const IntegerTypeSpec &make(KindParamValue kind) { + return helper.make(kind); + } + +private: + friend class KindedTypeHelper; + static KindedTypeHelper helper; + IntegerTypeSpec(KindParamValue kind) : NumericTypeSpec(kind) {} + friend std::ostream &operator<<(std::ostream &o, const IntegerTypeSpec &x); +}; + +// One unique instance of RealTypeSpec for each kind. +class RealTypeSpec : public NumericTypeSpec { +public: + static const RealTypeSpec &make() { return helper.make(); } + static const RealTypeSpec &make(KindParamValue kind) { + return helper.make(kind); + } + +private: + friend class KindedTypeHelper; + static KindedTypeHelper helper; + RealTypeSpec(KindParamValue kind) : NumericTypeSpec(kind) {} + friend std::ostream &operator<<(std::ostream &o, const RealTypeSpec &x); +}; + +// One unique instance of ComplexTypeSpec for each kind. +class ComplexTypeSpec : public NumericTypeSpec { +public: + static const ComplexTypeSpec &make() { return helper.make(); } + static const ComplexTypeSpec &make(KindParamValue kind) { + return helper.make(kind); + } + +private: + friend class KindedTypeHelper; + static KindedTypeHelper helper; + ComplexTypeSpec(KindParamValue kind) : NumericTypeSpec(kind) {} + friend std::ostream &operator<<(std::ostream &o, const ComplexTypeSpec &x); +}; + +class CharacterTypeSpec : public IntrinsicTypeSpec { +public: + static const int DefaultKind = 0; + CharacterTypeSpec(LenParamValue len, KindParamValue kind = DefaultKind) + : IntrinsicTypeSpec(kind), len_{len} {} + +private: + const LenParamValue len_; + friend std::ostream &operator<<(std::ostream &, const CharacterTypeSpec &); +}; + +// Definition of a type parameter +class TypeParamDef { +public: + TypeParamDef(const Name &name, const IntegerTypeSpec &type, + const std::optional &defaultValue = {}) + : name_{name}, type_{type}, defaultValue_{defaultValue} {}; + const Name &name() { return name_; } + const IntegerTypeSpec &type() { return type_; } + const std::optional &defaultValue() { return defaultValue_; } + +private: + const Name name_; + const IntegerTypeSpec type_; + const std::optional defaultValue_; +}; + +using TypeParamDefs = std::vector; + +// Definition of a derived type +class DerivedTypeDef { +public: + DerivedTypeDef(const Name &name, const Attrs &attrs = {}, + const TypeParamDefs &lenParams = {}, const TypeParamDefs &kindParams = {}, + bool private_ = false, bool sequence = false); + const Name name_; + const std::optional parent_ = {}; + const Attrs attrs_; + const TypeParamDefs lenParams_; + const TypeParamDefs kindParams_; + const bool private_ = false; + const bool sequence_ = false; + // TODO: components + // TODO: type-bound procedures + friend std::ostream &operator<<(std::ostream &, const DerivedTypeDef &); +}; + +using KindParamValues = std::map; +using LenParamValues = std::map; + +// Instantiation of a DerivedTypeDef with kind and len parameter values +class DerivedTypeSpec : public TypeSpec { +public: + DerivedTypeSpec(DerivedTypeDef def, KindParamValues kindParamValues = {}, + LenParamValues lenParamValues = {}); + +private: + const DerivedTypeDef def_; + const KindParamValues kindParamValues_; + const LenParamValues lenParamValues_; + friend std::ostream &operator<<(std::ostream &, const DerivedTypeSpec &); +}; + +class DeclTypeSpec { +public: + // intrinsic-type-spec or TYPE(intrinsic-type-spec) + static const DeclTypeSpec makeIntrinsic( + const IntrinsicTypeSpec *intrinsicTypeSpec) { + return DeclTypeSpec(Intrinsic, intrinsicTypeSpec, nullptr); + } + // TYPE(derived-type-spec) + static const DeclTypeSpec makeTypeDerivedType( + const DerivedTypeSpec *derivedTypeSpec) { + return DeclTypeSpec(TypeDerived, nullptr, derivedTypeSpec); + } + // CLASS(derived-type-spec) + static const DeclTypeSpec makeClassDerivedType( + const DerivedTypeSpec *derivedTypeSpec) { + return DeclTypeSpec(ClassDerived, nullptr, derivedTypeSpec); + } + // TYPE(*) or CLASS(*) + static const DeclTypeSpec makeUnlimitedPoly() { + return DeclTypeSpec(UnlimitedPoly, nullptr, nullptr); + } + + enum Category { Intrinsic, TypeDerived, ClassDerived, UnlimitedPoly }; + Category category() const { return category_; } + const IntrinsicTypeSpec &intrinsicTypeSpec() const { + return *intrinsicTypeSpec_; + } + const DerivedTypeSpec &derivedTypeSpec() const { return *derivedTypeSpec_; } + +private: + DeclTypeSpec(Category category, const IntrinsicTypeSpec *intrinsicTypeSpec, + const DerivedTypeSpec *derivedTypeSpec) + : category_{category}, intrinsicTypeSpec_{intrinsicTypeSpec}, + derivedTypeSpec_{derivedTypeSpec} {} + const Category category_; + const IntrinsicTypeSpec *const intrinsicTypeSpec_; + const DerivedTypeSpec *const derivedTypeSpec_; +}; + +struct DataComponentDef { + // component-array-spec + // coarray-spec + DataComponentDef( + const DeclTypeSpec type, const Name &name, const Attrs &attrs) + : type_{type}, name_{name}, attrs_{attrs} { + checkAttrs("DataComponentDef", attrs, + Attrs{Attr::PUBLIC, Attr::PRIVATE, Attr::ALLOCATABLE, Attr::CONTIGUOUS, + Attr::POINTER}); + } + +private: + const DeclTypeSpec type_; + const Name name_; + const Attrs attrs_; +}; + +// An array spec bound: an explicit integer expression or ASSUMED or DEFERRED +class Bound { +public: + static const Bound ASSUMED; + static const Bound DEFERRED; + Bound(const IntExpr &expr) : category_{Explicit}, expr_{expr.clone()} {} + bool isExplicit() const { return category_ == Explicit; } + bool isAssumed() const { return category_ == Assumed; } + bool isDeferred() const { return category_ == Deferred; } + const IntExpr &getExplicit() const { return *expr_; } + +private: + enum Category { Explicit, Deferred, Assumed }; + Bound(Category category) : category_{category}, expr_{&IntConst::ZERO} {} + const Category category_; + const IntExpr *const expr_; + friend std::ostream &operator<<(std::ostream &, const Bound &); +}; + +class ShapeSpec { +public: + // lb:ub + static ShapeSpec makeExplicit(const Bound &lb, const Bound &ub) { + return ShapeSpec(lb, ub); + } + // 1:ub + static const ShapeSpec makeExplicit(const Bound &ub) { + return makeExplicit(IntConst::ONE, ub); + } + // 1: or lb: + static ShapeSpec makeAssumed(const Bound &lb = IntConst::ONE) { + return ShapeSpec(lb, Bound::DEFERRED); + } + // : + static ShapeSpec makeDeferred() { + return ShapeSpec(Bound::DEFERRED, Bound::DEFERRED); + } + // 1:* or lb:* + static ShapeSpec makeImplied(const Bound &lb) { + return ShapeSpec(lb, Bound::ASSUMED); + } + // .. + static ShapeSpec makeAssumedRank() { + return ShapeSpec(Bound::ASSUMED, Bound::ASSUMED); + } + friend std::ostream &operator<<(std::ostream &, const ShapeSpec &); + +private: + ShapeSpec(const Bound &lb, const Bound &ub) : lb_{lb}, ub_{ub} {} + const Bound lb_; + const Bound ub_; +}; + +} // namespace Fortran + +#endif