[flang] checkpoint work on descriptors
authorpeter klausler <pklausler@nvidia.com>
Thu, 26 Jul 2018 23:07:50 +0000 (16:07 -0700)
committerpeter klausler <pklausler@nvidia.com>
Fri, 3 Aug 2018 23:23:58 +0000 (16:23 -0700)
Original-commit: flang-compiler/f18@5e68ebea25c2c1bef73270bf8d43144cd9d59c9f
Reviewed-on: https://github.com/flang-compiler/f18/pull/162
Tree-same-pre-rewrite: false

flang/runtime/ISO_Fortran_binding.cc
flang/runtime/descriptor.cc
flang/runtime/descriptor.h

index 8d2e62a..3fa70db 100644 (file)
@@ -48,7 +48,10 @@ int CFI_allocate(CFI_cdesc_t *descriptor, const CFI_index_t lower_bounds[],
   if (descriptor->rank > CFI_MAX_RANK) {
     return CFI_INVALID_RANK;
   }
-  // TODO: CFI_INVALID_TYPE?
+  if (descriptor->type < CFI_type_signed_char ||
+      descriptor->type > CFI_type_struct) {
+    return CFI_INVALID_TYPE;
+  }
   if (descriptor->type != CFI_type_cptr) {
     elem_len = descriptor->elem_len;
     if (elem_len <= 0) {
@@ -72,6 +75,7 @@ int CFI_allocate(CFI_cdesc_t *descriptor, const CFI_index_t lower_bounds[],
     return CFI_ERROR_MEM_ALLOCATION;
   }
   descriptor->base_addr = p;
+  descriptor->elem_len = elem_len;
   return CFI_SUCCESS;
 }
 
@@ -92,6 +96,48 @@ int CFI_deallocate(CFI_cdesc_t *descriptor) {
   return CFI_SUCCESS;
 }
 
+static constexpr std::size_t MinElemLen(CFI_type_t type) {
+  std::size_t minElemLen{0};
+  switch (type) {
+  case CFI_type_signed_char: minElemLen = sizeof(signed char); break;
+  case CFI_type_short: minElemLen = sizeof(short); break;
+  case CFI_type_int: minElemLen = sizeof(int); break;
+  case CFI_type_long: minElemLen = sizeof(long); break;
+  case CFI_type_long_long: minElemLen = sizeof(long long); break;
+  case CFI_type_size_t: minElemLen = sizeof(std::size_t); break;
+  case CFI_type_int8_t: minElemLen = sizeof(std::int8_t); break;
+  case CFI_type_int16_t: minElemLen = sizeof(std::int16_t); break;
+  case CFI_type_int32_t: minElemLen = sizeof(std::int32_t); break;
+  case CFI_type_int64_t: minElemLen = sizeof(std::int64_t); break;
+  case CFI_type_int128_t: minElemLen = 2 * sizeof(std::int64_t); break;
+  case CFI_type_int_least8_t: minElemLen = sizeof(std::int_least8_t); break;
+  case CFI_type_int_least16_t: minElemLen = sizeof(std::int_least16_t); break;
+  case CFI_type_int_least32_t: minElemLen = sizeof(std::int_least32_t); break;
+  case CFI_type_int_least64_t: minElemLen = sizeof(std::int_least64_t); break;
+  case CFI_type_int_least128_t:
+    minElemLen = 2 * sizeof(std::int_least64_t);
+    break;
+  case CFI_type_int_fast8_t: minElemLen = sizeof(std::int_fast8_t); break;
+  case CFI_type_int_fast16_t: minElemLen = sizeof(std::int_fast16_t); break;
+  case CFI_type_int_fast32_t: minElemLen = sizeof(std::int_fast32_t); break;
+  case CFI_type_int_fast64_t: minElemLen = sizeof(std::int_fast64_t); break;
+  case CFI_type_intmax_t: minElemLen = sizeof(std::intmax_t); break;
+  case CFI_type_intptr_t: minElemLen = sizeof(std::intptr_t); break;
+  case CFI_type_ptrdiff_t: minElemLen = sizeof(std::ptrdiff_t); break;
+  case CFI_type_float: minElemLen = sizeof(float); break;
+  case CFI_type_double: minElemLen = sizeof(double); break;
+  case CFI_type_long_double: minElemLen = sizeof(long double); break;
+  case CFI_type_float_Complex: minElemLen = 2 * sizeof(float); break;
+  case CFI_type_double_Complex: minElemLen = 2 * sizeof(double); break;
+  case CFI_type_long_double_Complex:
+    minElemLen = 2 * sizeof(long double);
+    break;
+  case CFI_type_Bool: minElemLen = 1; break;
+  case CFI_type_char: minElemLen = sizeof(char); break;
+  }
+  return minElemLen;
+}
+
 int CFI_establish(CFI_cdesc_t *descriptor, void *base_addr,
     CFI_attribute_t attribute, CFI_type_t type, std::size_t elem_len,
     CFI_rank_t rank, const CFI_index_t extents[]) {
@@ -107,9 +153,14 @@ int CFI_establish(CFI_cdesc_t *descriptor, void *base_addr,
   if (rank > 0 && base_addr != nullptr && extents == nullptr) {
     return CFI_INVALID_EXTENT;
   }
-  if (type != CFI_type_struct && type != CFI_type_other &&
-      type != CFI_type_cptr) {
-    // TODO: force value of elem_len
+  if (type < CFI_type_signed_char || type > CFI_type_struct) {
+    return CFI_INVALID_TYPE;
+  }
+  std::size_t minElemLen{MinElemLen(type)};
+  if (minElemLen > 0) {
+    elem_len = minElemLen;
+  } else if (elem_len <= 0) {
+    return CFI_INVALID_ELEM_LEN;
   }
   descriptor->base_addr = base_addr;
   descriptor->elem_len = elem_len;
index b5a4c5a..24bf7f2 100644 (file)
 // TODO: Not complete; exists to check compilability of descriptor.h
 
 #include "descriptor.h"
+#include <cstdlib>
 
 namespace Fortran::runtime {
 
-Descriptor::Descriptor(const DerivedTypeSpecialization &dts, int rank) {
-  raw_.base_addr = nullptr;
-  raw_.elem_len = dts.SizeInBytes();
-  raw_.version = CFI_VERSION;
-  raw_.rank = rank;
-  raw_.type = CFI_type_struct;
-  raw_.attribute = ADDENDUM;
-  Addendum()->set_derivedTypeSpecialization(&dts);
+TypeCode::TypeCode(TypeCode::Form f, int kind) {
+  switch (f) {
+  case Form::Integer:
+    switch (kind) {
+    case 1: raw_ = CFI_type_int8_t; break;
+    case 2: raw_ = CFI_type_int16_t; break;
+    case 4: raw_ = CFI_type_int32_t; break;
+    case 8: raw_ = CFI_type_int64_t; break;
+    case 16: raw_ = CFI_type_int128_t; break;
+    }
+    break;
+  case Form::Real:
+    switch (kind) {
+    case 4: raw_ = CFI_type_float; break;
+    case 8: raw_ = CFI_type_double; break;
+    case 10:
+    case 16: raw_ = CFI_type_long_double; break;
+    }
+    break;
+  case Form::Complex:
+    switch (kind) {
+    case 4: raw_ = CFI_type_float_Complex; break;
+    case 8: raw_ = CFI_type_double_Complex; break;
+    case 10:
+    case 16: raw_ = CFI_type_long_double_Complex; break;
+    }
+    break;
+  case Form::Character:
+    if (kind == 1) {
+      raw_ = CFI_type_cptr;
+    }
+    break;
+  case Form::Logical:
+    switch (kind) {
+    case 1: raw_ = CFI_type_Bool; break;
+    case 2: raw_ = CFI_type_int16_t; break;
+    case 4: raw_ = CFI_type_int32_t; break;
+    case 8: raw_ = CFI_type_int64_t; break;
+    }
+    break;
+  case Form::Derived: raw_ = CFI_type_struct; break;
+  }
+}
+
+std::size_t DescriptorAddendum::SizeInBytes() const {
+  return SizeInBytes(derivedTypeSpecialization_->derivedType().lenParameters());
+}
+
+void DescriptorView::SetDerivedTypeSpecialization(
+    const DerivedTypeSpecialization &dts) {
+  raw_.attribute |= ADDENDUM;
+  Addendum()->set_derivedTypeSpecialization(dts);
+}
+
+void DescriptorView::SetLenParameterValue(int which, TypeParameterValue x) {
+  raw_.attribute |= ADDENDUM;
+  Addendum()->SetLenParameterValue(which, x);
 }
 
-std::size_t Descriptor::SizeInBytes() const {
+std::size_t DescriptorView::SizeInBytes() const {
   const DescriptorAddendum *addendum{Addendum()};
-  return sizeof *this + raw_.rank * sizeof(Dimension) +
-      (addendum ? addendum->SizeOfAddendumInBytes() : 0);
+  return sizeof *this - sizeof(Dimension) + raw_.rank * sizeof(Dimension) +
+      (addendum ? addendum->SizeInBytes() : 0);
+}
+
+int DescriptorView::Establish(TypeCode t, std::size_t elementBytes, void *p,
+    int rank, const SubscriptValue *extent) {
+  return CFI_establish(
+      &raw_, p, CFI_attribute_other, t.raw(), elementBytes, rank, extent);
+}
+
+int DescriptorView::Establish(TypeCode::Form f, int kind, void *p, int rank,
+    const SubscriptValue *extent) {
+  std::size_t elementBytes = kind;
+  if (f == TypeCode::Form::Complex) {
+    elementBytes *= 2;
+  }
+  return ISO::CFI_establish(&raw_, p, CFI_attribute_other,
+      TypeCode(f, kind).raw(), elementBytes, rank, extent);
+}
+
+int DescriptorView::Establish(const DerivedTypeSpecialization &dts, void *p,
+    int rank, const SubscriptValue *extent) {
+  int result{ISO::CFI_establish(
+      &raw_, p, ADDENDUM, CFI_type_struct, dts.SizeInBytes(), rank, extent)};
+  if (result == CFI_SUCCESS) {
+    Addendum()->set_derivedTypeSpecialization(dts);
+  }
+  return result;
 }
 
-std::int64_t TypeParameter::KindParameterValue(
+TypeParameterValue TypeParameter::KindParameterValue(
     const DerivedTypeSpecialization &specialization) const {
   return specialization.KindParameterValue(which_);
 }
 
-std::int64_t TypeParameter::Value(const Descriptor &descriptor) const {
+TypeParameterValue TypeParameter::Value(
+    const DescriptorView &descriptor) const {
   const DescriptorAddendum &addendum{*descriptor.Addendum()};
   if (isLenTypeParameter_) {
     return addendum.LenParameterValue(which_);
@@ -47,4 +124,64 @@ std::int64_t TypeParameter::Value(const Descriptor &descriptor) const {
     return KindParameterValue(*addendum.derivedTypeSpecialization());
   }
 }
+
+bool DerivedType::IsNonTrivial() const {
+  if (kindParameters_ > 0 || lenParameters_ > 0 || typeBoundProcedures_ > 0 ||
+      definedAssignments_ > 0 || finalSubroutine_.host != 0) {
+    return true;
+  }
+  for (int j{0}; j < components_; ++j) {
+    if (component_[j].IsDescriptor()) {
+      return true;
+    }
+    if (const DescriptorView *
+        staticDescriptor{component_[j].staticDescriptor()}) {
+      if (const DescriptorAddendum * addendum{staticDescriptor->Addendum()}) {
+        if (const DerivedTypeSpecialization *
+            dts{addendum->derivedTypeSpecialization()}) {
+          if (dts->derivedType().IsNonTrivial()) {
+            return true;
+          }
+        }
+      }
+    }
+  }
+  return false;
+}
+
+Object::~Object() {
+  if (p_ != nullptr) {
+    // TODO final procedure calls and component destruction
+    delete reinterpret_cast<char *>(p_);
+    p_ = nullptr;
+  }
+}
+
+bool Object::Create(
+    TypeCode::Form f, int kind, int rank, const SubscriptValue *extent) {
+  if (f == TypeCode::Form::Character || f == TypeCode::Form::Derived) {
+    // TODO support these...
+    return false;
+  }
+  std::size_t descriptorBytes{DescriptorView::SizeInBytes(rank)};
+  std::size_t elementBytes = kind;
+  if (f == TypeCode::Form::Complex) {
+    elementBytes *= 2;
+  }
+  std::size_t elements{1};
+  for (int j{0}; j < rank; ++j) {
+    if (extent[j] < 0) {
+      return false;
+    }
+    elements *= static_cast<std::size_t>(extent[j]);
+  }
+  std::size_t totalBytes{descriptorBytes + elements * elementBytes};
+  char *p{reinterpret_cast<char *>(std::malloc(totalBytes))};
+  if (p == nullptr) {
+    return false;
+  }
+  p_ = reinterpret_cast<DescriptorView *>(p);
+  p_->Establish(f, kind, p + descriptorBytes, rank, extent);
+  return true;
+}
 }  // namespace Fortran::runtime
index 8e341a2..ff26c01 100644 (file)
 namespace Fortran::runtime {
 
 class DerivedTypeSpecialization;
-class DescriptorAddendum;
+
+using TypeParameterValue = ISO::CFI_index_t;
+using SubscriptValue = ISO::CFI_index_t;
 
 // A C++ view of the sole interoperable standard descriptor (ISO_cdesc_t)
 // and its type and per-dimension information.
 
 class TypeCode {
 public:
-  enum class Form { Integer, Real, Complex, Logical, Character, Derived };
+  enum class Form { Integer, Real, Complex, Character, Logical, Derived };
 
   TypeCode() {}
   explicit TypeCode(ISO::CFI_type_t t) : raw_{t} {}
+  TypeCode(Form, int);
+
   int raw() const { return raw_; }
 
   constexpr bool IsValid() const {
@@ -57,13 +61,11 @@ public:
     return raw_ >= CFI_type_float_Complex &&
         raw_ <= CFI_type_long_double_Complex;
   }
-  constexpr bool IsLogical() const { return raw_ == CFI_type_Bool; }
   constexpr bool IsCharacter() const { return raw_ == CFI_type_cptr; }
+  constexpr bool IsLogical() const { return raw_ == CFI_type_Bool; }
   constexpr bool IsDerived() const { return raw_ == CFI_type_struct; }
 
-  constexpr bool IsIntrinsic() const {
-    return IsValid() && !IsDerived();
-  }
+  constexpr bool IsIntrinsic() const { return IsValid() && !IsDerived(); }
 
   constexpr Form GetForm() const {
     if (IsInteger()) {
@@ -75,12 +77,12 @@ public:
     if (IsComplex()) {
       return Form::Complex;
     }
-    if (IsLogical()) {
-      return Form::Logical;
-    }
     if (IsCharacter()) {
       return Form::Character;
     }
+    if (IsLogical()) {
+      return Form::Logical;
+    }
     return Form::Derived;
   }
 
@@ -90,39 +92,78 @@ private:
 
 class Dimension {
 public:
-  std::int64_t LowerBound() const { return raw_.lower_bound; }
-  std::int64_t Extent() const { return raw_.extent; }
-  std::int64_t UpperBound() const { return LowerBound() + Extent() - 1; }
-  std::int64_t ByteStride() const { return raw_.sm; }
+  SubscriptValue LowerBound() const { return raw_.lower_bound; }
+  SubscriptValue Extent() const { return raw_.extent; }
+  SubscriptValue UpperBound() const { return LowerBound() + Extent() - 1; }
+  SubscriptValue ByteStride() const { return raw_.sm; }
 
 private:
   ISO::CFI_dim_t raw_;
 };
 static_assert(sizeof(Dimension) == sizeof(ISO::CFI_dim_t));
 
-class Descriptor {
+// The storage for this object follows the last used dim[] entry in a
+// DescriptorView (CFI_cdesc_t) generic descriptor; this is why that class
+// cannot be defined as a derivation or encapsulation of the standard
+// argument descriptor.  Space matters here, since dynamic descriptors
+// can serve as components of derived type instances.  The presence of
+// this structure is implied by (CFI_cdesc_t.attribute & ADDENDUM) != 0,
+// and the number of elements in the len_[] array is determined by
+// DerivedType::lenParameters().
+class DescriptorAddendum {
 public:
-  Descriptor(TypeCode t, std::size_t elementBytes, int rank = 0) {
-    raw_.base_addr = nullptr;
-    raw_.elem_len = elementBytes;
-    raw_.version = CFI_VERSION;
-    raw_.rank = rank;
-    raw_.type = t.raw();
-    raw_.attribute = 0;
+  explicit DescriptorAddendum(const DerivedTypeSpecialization &dts)
+    : derivedTypeSpecialization_{&dts} {}
+
+  DescriptorAddendum &set_derivedTypeSpecialization(
+      const DerivedTypeSpecialization &dts) {
+    derivedTypeSpecialization_ = &dts;
+    return *this;
   }
-  Descriptor(const DerivedTypeSpecialization &, int rank = 0);
 
-  void Check() const;
+  const DerivedTypeSpecialization *derivedTypeSpecialization() const {
+    return derivedTypeSpecialization_;
+  }
 
-  template<typename A> A &Element(std::size_t offset = 0) const {
-    auto p = reinterpret_cast<char *>(raw_.base_addr);
-    return *reinterpret_cast<A *>(p + offset);
+  TypeParameterValue LenParameterValue(int which) const { return len_[which]; }
+  static constexpr std::size_t SizeInBytes(int lenParameters) {
+    return sizeof(DescriptorAddendum) - sizeof(TypeParameterValue) +
+        lenParameters * sizeof(TypeParameterValue);
+  }
+  std::size_t SizeInBytes() const;
+
+  void SetLenParameterValue(int which, TypeParameterValue x) {
+    len_[which] = x;
   }
 
+private:
+  const DerivedTypeSpecialization *derivedTypeSpecialization_{nullptr};
+  TypeParameterValue len_[1];  // must be the last component
+  // The LEN type parameter values can also include captured values of
+  // specification expressions that were used for bounds and for LEN type
+  // parameters of components.  The values have been truncated to the LEN
+  // type parameter's type, if shorter than 64 bits, then sign-extended.
+};
+
+// A C++ view of a standard descriptor object.  Do not use for actually
+// allocating a descriptor, as its size cannot be known at compilation
+// time -- see Descriptor below for that.
+class DescriptorView {
+public:
+  DescriptorView() = delete;
+  ~DescriptorView() = delete;
+
+  ISO::CFI_cdesc_t &raw() { return raw_; }
+  const ISO::CFI_cdesc_t &raw() const { return raw_; }
   std::size_t ElementBytes() const { return raw_.elem_len; }
   int rank() const { return raw_.rank; }
   TypeCode type() const { return TypeCode{raw_.type}; }
 
+  DescriptorView &set_base_addr(void *p) {
+    raw_.base_addr = p;
+    return *this;
+  }
+
   bool IsPointer() const {
     return (raw_.attribute & CFI_attribute_pointer) != 0;
   }
@@ -151,6 +192,12 @@ public:
     return *reinterpret_cast<const Dimension *>(&raw_.dim[dim]);
   }
 
+  std::size_t SubscriptByteOffset(
+      int dim, SubscriptValue subscriptValue) const {
+    const Dimension &dimension{GetDimension(dim)};
+    return (subscriptValue - dimension.LowerBound()) * dimension.ByteStride();
+  }
+
   DescriptorAddendum *Addendum() {
     if ((raw_.attribute & ADDENDUM) != 0) {
       return reinterpret_cast<DescriptorAddendum *>(&GetDimension(rank()));
@@ -167,8 +214,36 @@ public:
     }
   }
 
+  void SetDerivedTypeSpecialization(const DerivedTypeSpecialization &);
+
+  void SetLenParameterValue(int, TypeParameterValue);
+
+  static constexpr std::size_t SizeInBytes(
+      int rank, bool nontrivialType = false, int lengthTypeParameters = 0) {
+    std::size_t bytes{sizeof(DescriptorView) - sizeof(Dimension)};
+    bytes += rank * sizeof(Dimension);
+    if (nontrivialType || lengthTypeParameters > 0) {
+      bytes += DescriptorAddendum::SizeInBytes(lengthTypeParameters);
+    }
+    return bytes;
+  }
   std::size_t SizeInBytes() const;
 
+  void Check() const;
+
+  int Establish(TypeCode t, std::size_t elementBytes, void *p = nullptr,
+      int rank = 0, const SubscriptValue *extent = nullptr);
+  int Establish(TypeCode::Form f, int kind, void *p = nullptr, int rank = 0,
+      const SubscriptValue *extent = nullptr);
+  int Establish(const DerivedTypeSpecialization &, void *p = nullptr,
+      int rank = 0, const SubscriptValue *extent = nullptr);
+  // TODO: creation of sections
+
+  template<typename A> A &Element(std::size_t offset = 0) const {
+    auto p = reinterpret_cast<char *>(raw_.base_addr);
+    return *reinterpret_cast<A *>(p + offset);
+  }
+
 private:
   // These values must coexist with the ISO_Fortran_binding.h definitions
   // for CFI_attribute_... values and fit in the "attribute" field of
@@ -187,7 +262,7 @@ private:
 
   ISO::CFI_cdesc_t raw_;
 };
-static_assert(sizeof(Descriptor) == sizeof(ISO::CFI_cdesc_t));
+static_assert(sizeof(DescriptorView) == sizeof(ISO::CFI_cdesc_t));
 
 // Static type information is suitable for loading in a read-only section.
 // Information about intrinsic types is inferable from raw CFI_type_t
@@ -200,18 +275,19 @@ public:
   const char *name() const { return name_; }
   const TypeCode typeCode() const { return typeCode_; }
   bool isLenTypeParameter() const { return isLenTypeParameter_; }
-  std::size_t which() const { return which_; }
-  std::int64_t defaultValue() const { return defaultValue_; }
+  int which() const { return which_; }
+  TypeParameterValue defaultValue() const { return defaultValue_; }
 
-  std::int64_t KindParameterValue(const DerivedTypeSpecialization &) const;
-  std::int64_t Value(const Descriptor &) const;
+  TypeParameterValue KindParameterValue(
+      const DerivedTypeSpecialization &) const;
+  TypeParameterValue Value(const DescriptorView &) const;
 
 private:
   const char *name_;
   TypeCode typeCode_;  // INTEGER, but not necessarily default kind
   bool isLenTypeParameter_;  // whether value is in dynamic descriptor
-  std::size_t which_;  // index of this parameter in kind/len array
-  std::int64_t defaultValue_;
+  int which_;  // index of this parameter in kind/len array
+  TypeParameterValue defaultValue_;
 };
 
 // Components that have any need for a descriptor will either reference
@@ -227,7 +303,7 @@ class Component {
 public:
   const char *name() const { return name_; }
   TypeCode typeCode() const { return typeCode_; }
-  const Descriptor *staticDescriptor() const { return staticDescriptor_; }
+  const DescriptorView *staticDescriptor() const { return staticDescriptor_; }
   bool IsParent() const { return (flags_ & PARENT) != 0; }
   bool IsPrivate() const { return (flags_ & PRIVATE) != 0; }
   bool IsDescriptor() const { return (flags_ & IS_DESCRIPTOR) != 0; }
@@ -237,7 +313,7 @@ private:
   const char *name_{nullptr};
   std::uint32_t flags_{0};
   TypeCode typeCode_{CFI_type_other};
-  const Descriptor *staticDescriptor_{nullptr};
+  const DescriptorView *staticDescriptor_{nullptr};
 };
 
 struct ExecutableCode {
@@ -267,20 +343,19 @@ struct DefinedAssignment {
 // the execution of FINAL subroutines.
 class DerivedType {
 public:
-  DerivedType(const char *n, std::size_t kps, std::size_t lps,
-      const TypeParameter *tp, std::size_t cs, const Component *ca,
-      std::size_t tbps, const TypeBoundProcedure *tbp, std::size_t das,
+  DerivedType(const char *n, int kps, int lps, const TypeParameter *tp, int cs,
+      const Component *ca, int tbps, const TypeBoundProcedure *tbp, int das,
       const DefinedAssignment *da)
     : name_{n}, kindParameters_{kps}, lenParameters_{lps}, components_{cs},
-      typeParameter_{tp}, typeBoundProcedure_{tbp}, definedAssignments_{das},
-      definedAssignment_{da} {}
+      typeParameter_{tp}, typeBoundProcedures_{tbps}, typeBoundProcedure_{tbp},
+      definedAssignments_{das}, definedAssignment_{da} {}
 
   const char *name() const { return name_; }
-  std::size_t kindParameters() const { return kindParameters_; }
-  std::size_t lenParameters() const { return lenParameters_; }
+  int kindParameters() const { return kindParameters_; }
+  int lenParameters() const { return lenParameters_; }
   const TypeParameter &typeParameter(int n) const { return typeParameter_[n]; }
-  std::size_t components() const { return components_; }
-  std::size_t typeBoundProcedures() const { return typeBoundProcedures_; }
+  int components() const { return components_; }
+  int typeBoundProcedures() const { return typeBoundProcedures_; }
   const TypeBoundProcedure &typeBoundProcedure(int n) const {
     return typeBoundProcedure_[n];
   }
@@ -302,6 +377,7 @@ public:
   bool AnyPrivate() const;
   bool IsSequence() const { return (flags_ & SEQUENCE) != 0; }
   bool IsBindC() const { return (flags_ & BIND_C) != 0; }
+  bool IsNonTrivial() const;
 
   // TODO: assignment
   // TODO: finalization
@@ -309,19 +385,19 @@ public:
 private:
   enum Flag { SEQUENCE = 1, BIND_C = 2 };
 
-  const char *name_;  // NUL-terminated constant text
+  const char *name_{""};  // NUL-terminated constant text
   std::uint64_t flags_{0};  // needed for IsSameType() correct semantics
-  std::size_t kindParameters_;
-  std::size_t lenParameters_;
-  std::size_t components_;  // *not* including type parameters
-  std::size_t typeBoundProcedures_;
-  const TypeParameter *typeParameter_;  // array
-  const Component *component_;  // array
-  const TypeBoundProcedure
-      *typeBoundProcedure_;  // array of overridable TBP bindings
+  int kindParameters_{0};
+  int lenParameters_{0};
+  int components_{0};  // *not* including type parameters
+  const TypeParameter *typeParameter_{nullptr};  // array
+  const Component *component_{nullptr};  // array
+  int typeBoundProcedures_{0};
+  const TypeBoundProcedure *typeBoundProcedure_{
+      nullptr};  // array of overridable TBP bindings
   ExecutableCode finalSubroutine_;  // can be null
-  std::size_t definedAssignments_;
-  const DefinedAssignment *definedAssignment_;  // array
+  int definedAssignments_{0};
+  const DefinedAssignment *definedAssignment_{nullptr};  // array
 };
 
 class ComponentSpecialization {
@@ -332,12 +408,12 @@ public:
   template<typename A> const A *Locate(const char *instance) const {
     return reinterpret_cast<const A *>(instance + offset_);
   }
-  const Descriptor *GetDescriptor(
+  const DescriptorView *GetDescriptorView(
       const Component &c, const char *instance) const {
-    if (const Descriptor * staticDescriptor{c.staticDescriptor()}) {
+    if (const DescriptorView * staticDescriptor{c.staticDescriptor()}) {
       return staticDescriptor;
     } else if (c.IsDescriptor()) {
-      return Locate<const Descriptor>(instance);
+      return Locate<const DescriptorView>(instance);
     } else {
       return nullptr;
     }
@@ -354,14 +430,14 @@ private:
 class DerivedTypeSpecialization {
 public:
   DerivedTypeSpecialization(const DerivedType &dt, std::size_t n,
-      const char *init, const std::int64_t *kp,
+      const char *init, const TypeParameterValue *kp,
       const ComponentSpecialization *cs)
     : derivedType_{dt}, bytes_{n}, initializer_{init}, kindParameterValue_{kp},
       componentSpecialization_{cs} {}
   const DerivedType &derivedType() const { return derivedType_; }
 
   std::size_t SizeInBytes() const { return bytes_; }
-  std::int64_t KindParameterValue(int n) const {
+  TypeParameterValue KindParameterValue(int n) const {
     return kindParameterValue_[n];
   }
   const ComponentSpecialization &GetComponent(int n) const {
@@ -376,61 +452,69 @@ private:
   const DerivedType &derivedType_;
   std::size_t bytes_;  // allocation size of one scalar instance, w/ alignment
   const char *initializer_;  // can be null; includes base components
-  const std::int64_t *kindParameterValue_;  // array
+  const TypeParameterValue *kindParameterValue_;  // array
   const ComponentSpecialization *componentSpecialization_;  // array
 };
 
-// The storage for this object follows the last used dim[] entry in a
-// Descriptor (CFI_cdesc_t) generic descriptor; that is why this class
-// cannot be defined as a derivation or encapsulation of the standard
-// argument descriptor.  Space matters here, since dynamic descriptors
-// can serve as components of derived type instances.  The presence of
-// this structure is implied by (CFI_cdesc_t.attribute & ADDENDUM) != 0,
-// and the number of elements in the len_[] array is determined by
-// DerivedType::lenParameters().
-class DescriptorAddendum {
-public:
-  explicit DescriptorAddendum(const DerivedTypeSpecialization *dts)
-    : derivedTypeSpecialization_{dts} {}
+// Procedure pointers have static links for host association.
+// TODO: define the target data structure of that static link
+struct ProcedurePointer {
+  ExecutableCode entryAddresses;
+  void *staticLink;
+};
 
-  DescriptorAddendum &set_derivedTypeSpecialization(
-      const DerivedTypeSpecialization *dts) {
-    derivedTypeSpecialization_ = dts;
-    return *this;
-  }
+// TODO: coarray hooks
 
-  const DerivedTypeSpecialization *derivedTypeSpecialization() const {
-    return derivedTypeSpecialization_;
+template<int MAX_RANK = CFI_MAX_RANK,
+    bool NONTRIVIAL_DERIVED_TYPE_ALLOWED = false, int MAX_LEN_PARMS = 0>
+class alignas(DescriptorView) Descriptor {
+public:
+  static constexpr int maxRank{MAX_RANK};
+  static constexpr int maxLengthTypeParameters{MAX_LEN_PARMS};
+  static constexpr bool hasAddendum{
+      NONTRIVIAL_DERIVED_TYPE_ALLOWED || MAX_LEN_PARMS > 0};
+
+  Descriptor(TypeCode t, std::size_t elementBytes, int rank = maxRank,
+      const SubscriptValue *extent = nullptr) {
+    View().Establish(t, elementBytes, rank, extent);
   }
-
-  std::int64_t LenParameterValue(std::size_t n) const { return len_[n]; }
-  std::size_t SizeOfAddendumInBytes() const {
-    return sizeof *this - sizeof len_[0] +
-        derivedTypeSpecialization_->derivedType().lenParameters() *
-        sizeof len_[0];
+  Descriptor(TypeCode::Form f, int kind, int rank = maxRank,
+      const SubscriptValue *extent = nullptr) {
+    View().Establish(f, kind, rank, extent);
+  }
+  Descriptor(const DerivedTypeSpecialization &dts, int rank = maxRank,
+      const SubscriptValue *extent = nullptr) {
+    View().Establish(dts, rank, extent);
   }
 
-  void SetLenParameterValue(std::size_t which, std::int64_t x) {
-    len_[which] = x;
+  DescriptorView &View() {
+    return *reinterpret_cast<DescriptorView *>(storage_);
+  }
+  const DescriptorView &View() const {
+    return *reinterpret_cast<const DescriptorView *>(storage_);
   }
 
 private:
-  const DerivedTypeSpecialization *derivedTypeSpecialization_{nullptr};
-  std::int64_t len_[1];  // must be the last component
-  // The LEN type parameter values can also include captured values of
-  // specification expressions that were used for bounds and for LEN type
-  // parameters of components.  The values have been truncated to the LEN
-  // type parameter's type, if shorter than 64 bits, then sign-extended.
+  static constexpr std::size_t byteSize{DescriptorView::SizeInBytes(
+      maxRank, hasAddendum, maxLengthTypeParameters)};
+  char storage_[byteSize];
 };
 
-// Procedure pointers have static links for host association.
-// TODO: define the target data structure of that static link
-struct ProcedurePointer {
-  ExecutableCode entryAddresses;
-  void *staticLink;
-};
+// A owning pointer to a whole object whose data are contiguous with and
+// preceded in memory by a descriptor.
+class Object {
+public:
+  ~Object();
+  bool Create(TypeCode::Form f, int kind, int rank = 0,
+      const SubscriptValue *extent = nullptr);
+  bool Create(const DerivedTypeSpecialization &, int rank = 0,
+      const SubscriptValue *lenParamsAndExtents = nullptr);
+  bool Exists() const { return p_ != nullptr; }
+  const DescriptorView &descriptorView() const { return *p_; }
 
-// TODO: coarray hooks
+private:
+  DescriptorView *p_;
+};
 
 }  // namespace Fortran::runtime
 #endif  // FORTRAN_RUNTIME_DESCRIPTOR_H_