//===-- runtime/type-info.h -------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_RUNTIME_TYPE_INFO_H_
#define FORTRAN_RUNTIME_TYPE_INFO_H_
// A C++ perspective of the derived type description schemata in
// flang/module/__fortran_type_info.f90.
#include "terminator.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/bit-population-count.h"
#include "flang/Common/optional.h"
#include "flang/Runtime/descriptor.h"
#include <cinttypes>
#include <memory>
namespace Fortran::runtime::typeInfo {
class DerivedType;
using ProcedurePointer = void (*)(); // TYPE(C_FUNPTR)
struct Binding {
ProcedurePointer proc;
StaticDescriptor<0> name; // CHARACTER(:), POINTER
};
class Value {
public:
enum class Genre : std::uint8_t {
Deferred = 1,
Explicit = 2,
LenParameter = 3
};
RT_API_ATTRS Genre genre() const { return genre_; }
RT_API_ATTRS Fortran::common::optional<TypeParameterValue> GetValue(
const Descriptor *) const;
private:
Genre genre_{Genre::Explicit};
// The value encodes an index into the table of LEN type parameters in
// a descriptor's addendum for genre == Genre::LenParameter.
TypeParameterValue value_{0};
};
class Component {
public:
enum class Genre : std::uint8_t {
Data = 1,
Pointer = 2,
Allocatable = 3,
Automatic = 4
};
RT_API_ATTRS const Descriptor &name() const { return name_.descriptor(); }
RT_API_ATTRS Genre genre() const { return genre_; }
RT_API_ATTRS TypeCategory category() const {
return static_cast<TypeCategory>(category_);
}
RT_API_ATTRS int kind() const { return kind_; }
RT_API_ATTRS int rank() const { return rank_; }
RT_API_ATTRS std::uint64_t offset() const { return offset_; }
RT_API_ATTRS const Value &characterLen() const { return characterLen_; }
RT_API_ATTRS const DerivedType *derivedType() const {
return derivedType_.descriptor().OffsetElement<const DerivedType>();
}
RT_API_ATTRS const Value *lenValue() const {
return lenValue_.descriptor().OffsetElement<const Value>();
}
RT_API_ATTRS const Value *bounds() const {
return bounds_.descriptor().OffsetElement<const Value>();
}
RT_API_ATTRS const char *initialization() const { return initialization_; }
RT_API_ATTRS std::size_t GetElementByteSize(const Descriptor &) const;
RT_API_ATTRS std::size_t GetElements(const Descriptor &) const;
// For components that are descriptors, returns size of descriptor;
// for Genre::Data, returns elemental byte size times element count.
RT_API_ATTRS std::size_t SizeInBytes(const Descriptor &) const;
// Establishes a descriptor from this component description.
RT_API_ATTRS void EstablishDescriptor(
Descriptor &, const Descriptor &container, Terminator &) const;
// Creates a pointer descriptor from this component description, possibly
// with subscripts
RT_API_ATTRS void CreatePointerDescriptor(Descriptor &,
const Descriptor &container, Terminator &,
const SubscriptValue * = nullptr) const;
FILE *Dump(FILE * = stdout) const;
private:
StaticDescriptor<0> name_; // CHARACTER(:), POINTER
Genre genre_{Genre::Data};
std::uint8_t category_; // common::TypeCategory
std::uint8_t kind_{0};
std::uint8_t rank_{0};
std::uint64_t offset_{0};
Value characterLen_; // for TypeCategory::Character
StaticDescriptor<0, true> derivedType_; // TYPE(DERIVEDTYPE), POINTER
StaticDescriptor<1, true>
lenValue_; // TYPE(VALUE), POINTER, DIMENSION(:), CONTIGUOUS
StaticDescriptor<2, true>
bounds_; // TYPE(VALUE), POINTER, DIMENSION(2,:), CONTIGUOUS
const char *initialization_{nullptr}; // for Genre::Data and Pointer
// TODO: cobounds
// TODO: `PRIVATE` attribute
};
struct ProcPtrComponent {
StaticDescriptor<0> name; // CHARACTER(:), POINTER
std::uint64_t offset{0};
ProcedurePointer procInitialization;
};
class SpecialBinding {
public:
enum class Which : std::uint8_t {
None = 0,
ScalarAssignment = 1,
ElementalAssignment = 2,
ReadFormatted = 3,
ReadUnformatted = 4,
WriteFormatted = 5,
WriteUnformatted = 6,
ElementalFinal = 7,
AssumedRankFinal = 8,
ScalarFinal = 9,
// higher-ranked final procedures follow
};
// Special bindings can be created during execution to handle defined
// I/O procedures that are not type-bound.
RT_API_ATTRS SpecialBinding(Which which, ProcedurePointer proc,
std::uint8_t isArgDescSet, std::uint8_t isTypeBound,
std::uint8_t isArgContiguousSet)
: which_{which}, isArgDescriptorSet_{isArgDescSet},
isTypeBound_{isTypeBound}, isArgContiguousSet_{isArgContiguousSet},
proc_{proc} {}
static constexpr RT_API_ATTRS Which RankFinal(int rank) {
return static_cast<Which>(static_cast<int>(Which::ScalarFinal) + rank);
}
RT_API_ATTRS Which which() const { return which_; }
RT_API_ATTRS bool IsArgDescriptor(int zeroBasedArg) const {
return (isArgDescriptorSet_ >> zeroBasedArg) & 1;
}
RT_API_ATTRS bool isTypeBound() const { return isTypeBound_; }
RT_API_ATTRS bool IsArgContiguous(int zeroBasedArg) const {
return (isArgContiguousSet_ >> zeroBasedArg) & 1;
}
template <typename PROC> RT_API_ATTRS PROC GetProc() const {
return reinterpret_cast<PROC>(proc_);
}
FILE *Dump(FILE *) const;
private:
Which which_{Which::None};
// The following little bit-set identifies which dummy arguments are
// passed via descriptors for their derived type arguments.
// Which::Assignment and Which::ElementalAssignment:
// Set to 1, 2, or (usually 3).
// The passed-object argument (usually the "to") is always passed via a
// a descriptor in the cases where the runtime will call a defined
// assignment because these calls are to type-bound generics,
// not generic interfaces, and type-bound generic defined assigment
// may appear only in an extensible type and requires a passed-object
// argument (see C774), and passed-object arguments to TBPs must be
// both polymorphic and scalar (C760). The non-passed-object argument
// (usually the "from") is usually, but not always, also a descriptor.
// Which::Final and Which::ElementalFinal:
// Set to 1 when dummy argument is assumed-shape; otherwise, the
// argument can be passed by address. (Fortran guarantees that
// any finalized object must be whole and contiguous by restricting
// the use of DEALLOCATE on pointers. The dummy argument of an
// elemental final subroutine must be scalar and monomorphic, but
// use a descriptors when the type has LEN parameters.)
// Which::AssumedRankFinal: flag must necessarily be set
// Defined I/O:
// Set to 1 when "dtv" initial dummy argument is polymorphic, which is
// the case when and only when the derived type is extensible.
// When false, the defined I/O subroutine must have been
// called via a generic interface, not a generic TBP.
std::uint8_t isArgDescriptorSet_{0};
std::uint8_t isTypeBound_{0};
// True when a FINAL subroutine has a dummy argument that is an array that
// is CONTIGUOUS or neither assumed-rank nor assumed-shape.
std::uint8_t isArgContiguousSet_{0};
ProcedurePointer proc_{nullptr};
};
class DerivedType {
public:
~DerivedType(); // never defined
RT_API_ATTRS const Descriptor &binding() const {
return binding_.descriptor();
}
RT_API_ATTRS const Descriptor &name() const { return name_.descriptor(); }
RT_API_ATTRS std::uint64_t sizeInBytes() const { return sizeInBytes_; }
RT_API_ATTRS const Descriptor &uninstatiated() const {
return uninstantiated_.descriptor();
}
RT_API_ATTRS const Descriptor &kindParameter() const {
return kindParameter_.descriptor();
}
RT_API_ATTRS const Descriptor &lenParameterKind() const {
return lenParameterKind_.descriptor();
}
RT_API_ATTRS const Descriptor &component() const {
return component_.descriptor();
}
RT_API_ATTRS const Descriptor &procPtr() const {
return procPtr_.descriptor();
}
RT_API_ATTRS const Descriptor &special() const {
return special_.descriptor();
}
RT_API_ATTRS bool hasParent() const { return hasParent_; }
RT_API_ATTRS bool noInitializationNeeded() const {
return noInitializationNeeded_;
}
RT_API_ATTRS bool noDestructionNeeded() const { return noDestructionNeeded_; }
RT_API_ATTRS bool noFinalizationNeeded() const {
return noFinalizationNeeded_;
}
RT_API_ATTRS std::size_t LenParameters() const {
return lenParameterKind().Elements();
}
RT_API_ATTRS const DerivedType *GetParentType() const;
// Finds a data component by name in this derived type or its ancestors.
RT_API_ATTRS const Component *FindDataComponent(
const char *name, std::size_t nameLen) const;
// O(1) look-up of special procedure bindings
RT_API_ATTRS const SpecialBinding *FindSpecialBinding(
SpecialBinding::Which which) const {
auto bitIndex{static_cast<std::uint32_t>(which)};
auto bit{std::uint32_t{1} << bitIndex};
if (specialBitSet_ & bit) {
// The index of this special procedure in the sorted array is the
// number of special bindings that are present with smaller "which"
// code values.
int offset{common::BitPopulationCount(specialBitSet_ & (bit - 1))};
const auto *binding{
special_.descriptor().ZeroBasedIndexedElement<SpecialBinding>(
offset)};
INTERNAL_CHECK(binding && binding->which() == which);
return binding;
} else {
return nullptr;
}
}
FILE *Dump(FILE * = stdout) const;
private:
// This member comes first because it's used like a vtable by generated code.
// It includes all of the ancestor types' bindings, if any, first,
// with any overrides from descendants already applied to them. Local
// bindings then follow in alphabetic order of binding name.
StaticDescriptor<1, true>
binding_; // TYPE(BINDING), DIMENSION(:), POINTER, CONTIGUOUS
StaticDescriptor<0> name_; // CHARACTER(:), POINTER
std::uint64_t sizeInBytes_{0};
// Instantiations of a parameterized derived type with KIND type
// parameters will point this data member to the description of
// the original uninstantiated type, which may be shared from a
// module via use association. The original uninstantiated derived
// type description will point to itself. Derived types that have
// no KIND type parameters will have a null pointer here.
StaticDescriptor<0, true> uninstantiated_; // TYPE(DERIVEDTYPE), POINTER
// These pointer targets include all of the items from the parent, if any.
StaticDescriptor<1> kindParameter_; // pointer to rank-1 array of INTEGER(8)
StaticDescriptor<1>
lenParameterKind_; // pointer to rank-1 array of INTEGER(1)
// This array of local data components includes the parent component.
// Components are in component order, not collation order of their names.
// It does not include procedure pointer components.
StaticDescriptor<1, true>
component_; // TYPE(COMPONENT), POINTER, DIMENSION(:), CONTIGUOUS
// Procedure pointer components
StaticDescriptor<1, true>
procPtr_; // TYPE(PROCPTR), POINTER, DIMENSION(:), CONTIGUOUS
// Packed in ascending order of "which" code values.
// Does not include special bindings from ancestral types.
StaticDescriptor<1, true>
special_; // TYPE(SPECIALBINDING), POINTER, DIMENSION(:), CONTIGUOUS
// Little-endian bit-set of special procedure binding "which" code values
// for O(1) look-up in FindSpecialBinding() above.
std::uint32_t specialBitSet_{0};
// Flags
bool hasParent_{false};
bool noInitializationNeeded_{false};
bool noDestructionNeeded_{false};
bool noFinalizationNeeded_{false};
};
} // namespace Fortran::runtime::typeInfo
#endif // FORTRAN_RUNTIME_TYPE_INFO_H_