[flang] Initial work on the representation of types.
authorTim Keith <tkeith@nvidia.com>
Wed, 7 Feb 2018 00:46:29 +0000 (16:46 -0800)
committerTim Keith <tkeith@nvidia.com>
Wed, 7 Feb 2018 00:46:29 +0000 (16:46 -0800)
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

flang/CMakeLists.txt
flang/Makefile
flang/attr.cc [new file with mode: 0644]
flang/attr.h [new file with mode: 0644]
flang/type.cc [new file with mode: 0644]
flang/type.h [new file with mode: 0644]

index 1d59b87..08bd5f6 100644 (file)
@@ -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)
index 5d0d242..c575021 100644 (file)
@@ -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 (file)
index 0000000..cc9564c
--- /dev/null
@@ -0,0 +1,52 @@
+#include "attr.h"
+#include <sstream>
+#include <string>
+
+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 (file)
index 0000000..e1d7aca
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef FORTRAN_ATTR_H_
+#define FORTRAN_ATTR_H_
+
+#include <iostream>
+#include <set>
+#include <string>
+
+#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<Attr>;
+
+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 (file)
index 0000000..6904be5
--- /dev/null
@@ -0,0 +1,250 @@
+#include <iostream>
+
+#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<typename V>
+static void checkParams(
+    std::string kindOrLen, TypeParamDefs defs, std::map<Name, V> values) {
+  std::set<Name> 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> LogicalTypeSpec::helper{"LOGICAL", 0};
+std::ostream &operator<<(std::ostream &o, const LogicalTypeSpec &x) {
+  return LogicalTypeSpec::helper.output(o, x);
+}
+
+KindedTypeHelper<IntegerTypeSpec> IntegerTypeSpec::helper{"INTEGER", 0};
+std::ostream &operator<<(std::ostream &o, const IntegerTypeSpec &x) {
+  return IntegerTypeSpec::helper.output(o, x);
+}
+
+KindedTypeHelper<RealTypeSpec> RealTypeSpec::helper{"REAL", 0};
+std::ostream &operator<<(std::ostream &o, const RealTypeSpec &x) {
+  return RealTypeSpec::helper.output(o, x);
+}
+
+KindedTypeHelper<ComplexTypeSpec> 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 (file)
index 0000000..1553c73
--- /dev/null
@@ -0,0 +1,392 @@
+#ifndef FORTRAN_TYPE_H_
+#define FORTRAN_TYPE_H_
+
+#include <algorithm>
+#include <list>
+#include <map>
+#include <memory>
+#include <optional>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#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<const IntExpr> 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<typename T> class KindedTypeHelper {
+public:
+  std::map<KindParamValue, T> 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<LogicalTypeSpec>;
+  static KindedTypeHelper<LogicalTypeSpec> 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<IntegerTypeSpec>;
+  static KindedTypeHelper<IntegerTypeSpec> 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<RealTypeSpec>;
+  static KindedTypeHelper<RealTypeSpec> 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<ComplexTypeSpec>;
+  static KindedTypeHelper<ComplexTypeSpec> 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<IntConst> &defaultValue = {})
+    : name_{name}, type_{type}, defaultValue_{defaultValue} {};
+  const Name &name() { return name_; }
+  const IntegerTypeSpec &type() { return type_; }
+  const std::optional<IntConst> &defaultValue() { return defaultValue_; }
+
+private:
+  const Name name_;
+  const IntegerTypeSpec type_;
+  const std::optional<IntConst> defaultValue_;
+};
+
+using TypeParamDefs = std::vector<TypeParamDef>;
+
+// 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<Name> 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<Name, KindParamValue>;
+using LenParamValues = std::map<Name, LenParamValue>;
+
+// 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