blob: a60d0b90da467665b3e9f392b6af18ce53aa4cdd [file] [log] [blame] [edit]
//===-- lib/runtime/descriptor-io.cpp ---------------------------*- 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
//
//===----------------------------------------------------------------------===//
#include "descriptor-io.h"
#include "edit-input.h"
#include "edit-output.h"
#include "unit.h"
#include "flang-rt/runtime/descriptor.h"
#include "flang-rt/runtime/io-stmt.h"
#include "flang-rt/runtime/namelist.h"
#include "flang-rt/runtime/terminator.h"
#include "flang-rt/runtime/type-info.h"
#include "flang-rt/runtime/work-queue.h"
#include "flang/Common/optional.h"
#include "flang/Common/restorer.h"
#include "flang/Common/uint128.h"
#include "flang/Runtime/cpp-type.h"
#include "flang/Runtime/freestanding-tools.h"
// Implementation of I/O data list item transfers based on descriptors.
// (All I/O items come through here so that the code is exercised for test;
// some scalar I/O data transfer APIs could be changed to bypass their use
// of descriptors in the future for better efficiency.)
namespace Fortran::runtime::io::descr {
RT_OFFLOAD_API_GROUP_BEGIN
template <typename A>
inline RT_API_ATTRS A &ExtractElement(IoStatementState &io,
const Descriptor &descriptor, const SubscriptValue subscripts[]) {
A *p{descriptor.Element<A>(subscripts)};
if (!p) {
io.GetIoErrorHandler().Crash("Bad address for I/O item -- null base "
"address or subscripts out of range");
}
return *p;
}
// Defined formatted I/O (maybe)
static RT_API_ATTRS Fortran::common::optional<bool> DefinedFormattedIo(
IoStatementState &io, const Descriptor &descriptor,
const typeInfo::DerivedType &derived,
const typeInfo::SpecialBinding &special,
const SubscriptValue subscripts[]) {
// Look at the next data edit descriptor. If this is list-directed I/O, the
// "maxRepeat=0" argument will prevent the input from advancing over an
// initial '(' that shouldn't be consumed now as the start of a real part.
Fortran::common::optional<DataEdit> peek{io.GetNextDataEdit(/*maxRepeat=*/0)};
if (peek &&
(peek->descriptor == DataEdit::DefinedDerivedType ||
peek->descriptor == DataEdit::ListDirected ||
peek->descriptor == DataEdit::ListDirectedRealPart)) {
// Defined formatting
IoErrorHandler &handler{io.GetIoErrorHandler()};
DataEdit edit{peek->descriptor == DataEdit::ListDirectedRealPart
? *peek
: *io.GetNextDataEdit(1)};
char ioType[2 + edit.maxIoTypeChars];
auto ioTypeLen{std::size_t{2} /*"DT"*/ + edit.ioTypeChars};
if (edit.descriptor == DataEdit::DefinedDerivedType) {
ioType[0] = 'D';
ioType[1] = 'T';
std::memcpy(ioType + 2, edit.ioType, edit.ioTypeChars);
} else {
runtime::strcpy(
ioType, io.mutableModes().inNamelist ? "NAMELIST" : "LISTDIRECTED");
ioTypeLen = runtime::strlen(ioType);
}
// V_LIST= argument
StaticDescriptor<1, true> vListStatDesc;
Descriptor &vListDesc{vListStatDesc.descriptor()};
bool integer8{special.specialCaseFlag()};
std::int64_t vList64[edit.maxVListEntries];
if (integer8) {
// Convert v_list values to INTEGER(8)
for (int j{0}; j < edit.vListEntries; ++j) {
vList64[j] = edit.vList[j];
}
vListDesc.Establish(
TypeCategory::Integer, sizeof(std::int64_t), nullptr, 1);
vListDesc.set_base_addr(vList64);
vListDesc.GetDimension(0).SetBounds(1, edit.vListEntries);
vListDesc.GetDimension(0).SetByteStride(
static_cast<SubscriptValue>(sizeof(std::int64_t)));
} else {
vListDesc.Establish(TypeCategory::Integer, sizeof(int), nullptr, 1);
vListDesc.set_base_addr(edit.vList);
vListDesc.GetDimension(0).SetBounds(1, edit.vListEntries);
vListDesc.GetDimension(0).SetByteStride(
static_cast<SubscriptValue>(sizeof(int)));
}
ExternalFileUnit *actualExternal{io.GetExternalFileUnit()};
ExternalFileUnit *external{actualExternal};
if (!external) {
// Create a new unit to service defined I/O for an
// internal I/O parent.
external = &ExternalFileUnit::NewUnit(handler, true);
}
ChildIo &child{external->PushChildIo(io)};
// Child formatted I/O is nonadvancing by definition (F'2018 12.6.2.4).
auto restorer{common::ScopedSet(io.mutableModes().nonAdvancing, true)};
std::int32_t unit{external->unitNumber()};
std::int32_t ioStat{IostatOk};
char ioMsg[100];
Fortran::common::optional<std::int64_t> startPos;
if (edit.descriptor == DataEdit::DefinedDerivedType &&
special.which() == typeInfo::SpecialBinding::Which::ReadFormatted) {
// DT is an edit descriptor, so everything that the child
// I/O subroutine reads counts towards READ(SIZE=).
startPos = io.InquirePos();
}
const auto *bindings{
derived.binding().OffsetElement<const typeInfo::Binding>()};
if (special.IsArgDescriptor(0)) {
// "dtv" argument is "class(t)", pass a descriptor
StaticDescriptor<1, true, 10 /*?*/> elementStatDesc;
Descriptor &elementDesc{elementStatDesc.descriptor()};
elementDesc.Establish(
derived, nullptr, 0, nullptr, CFI_attribute_pointer);
elementDesc.set_base_addr(descriptor.Element<char>(subscripts));
if (integer8) { // 64-bit UNIT=/IOSTAT=
std::int64_t unit64{unit};
std::int64_t ioStat64{ioStat};
auto *p{special.GetProc<void (*)(const Descriptor &, std::int64_t &,
char *, const Descriptor &, std::int64_t &, char *, std::size_t,
std::size_t)>(bindings)};
p(elementDesc, unit64, ioType, vListDesc, ioStat64, ioMsg, ioTypeLen,
sizeof ioMsg);
ioStat = ioStat64;
} else { // 32-bit UNIT=/IOSTAT=
auto *p{special.GetProc<void (*)(const Descriptor &, std::int32_t &,
char *, const Descriptor &, std::int32_t &, char *, std::size_t,
std::size_t)>(bindings)};
p(elementDesc, unit, ioType, vListDesc, ioStat, ioMsg, ioTypeLen,
sizeof ioMsg);
}
} else {
// "dtv" argument is "type(t)", pass a raw pointer
if (integer8) { // 64-bit UNIT= and IOSTAT=
std::int64_t unit64{unit};
std::int64_t ioStat64{ioStat};
auto *p{special.GetProc<void (*)(const void *, std::int64_t &, char *,
const Descriptor &, std::int64_t &, char *, std::size_t,
std::size_t)>(bindings)};
p(descriptor.Element<char>(subscripts), unit64, ioType, vListDesc,
ioStat64, ioMsg, ioTypeLen, sizeof ioMsg);
ioStat = ioStat64;
} else { // 32-bit UNIT= and IOSTAT=
auto *p{special.GetProc<void (*)(const void *, std::int32_t &, char *,
const Descriptor &, std::int32_t &, char *, std::size_t,
std::size_t)>(bindings)};
p(descriptor.Element<char>(subscripts), unit, ioType, vListDesc, ioStat,
ioMsg, ioTypeLen, sizeof ioMsg);
}
}
handler.Forward(ioStat, ioMsg, sizeof ioMsg);
external->PopChildIo(child);
if (!actualExternal) {
// Close unit created for internal I/O above.
auto *closing{external->LookUpForClose(external->unitNumber())};
RUNTIME_CHECK(handler, external == closing);
external->DestroyClosed();
}
if (startPos) {
io.GotChar(io.InquirePos() - *startPos);
}
return handler.GetIoStat() == IostatOk;
} else {
// There's a defined I/O subroutine, but there's a FORMAT present and
// it does not have a DT data edit descriptor, so apply default formatting
// to the components of the derived type as usual.
return Fortran::common::nullopt;
}
}
// Defined unformatted I/O
static RT_API_ATTRS bool DefinedUnformattedIo(IoStatementState &io,
const Descriptor &descriptor, const typeInfo::DerivedType &derived,
const typeInfo::SpecialBinding &special) {
// Unformatted I/O must have an external unit (or child thereof).
IoErrorHandler &handler{io.GetIoErrorHandler()};
ExternalFileUnit *external{io.GetExternalFileUnit()};
if (!external) { // INQUIRE(IOLENGTH=)
handler.SignalError(IostatNonExternalDefinedUnformattedIo);
return false;
}
ChildIo &child{external->PushChildIo(io)};
int unit{external->unitNumber()};
int ioStat{IostatOk};
char ioMsg[100];
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
const auto *bindings{
derived.binding().OffsetElement<const typeInfo::Binding>()};
if (special.IsArgDescriptor(0)) {
// "dtv" argument is "class(t)", pass a descriptor
auto *p{special.GetProc<void (*)(
const Descriptor &, int &, int &, char *, std::size_t)>(bindings)};
StaticDescriptor<1, true, 10 /*?*/> elementStatDesc;
Descriptor &elementDesc{elementStatDesc.descriptor()};
elementDesc.Establish(derived, nullptr, 0, nullptr, CFI_attribute_pointer);
for (; numElements-- > 0; descriptor.IncrementSubscripts(subscripts)) {
elementDesc.set_base_addr(descriptor.Element<char>(subscripts));
p(elementDesc, unit, ioStat, ioMsg, sizeof ioMsg);
if (ioStat != IostatOk) {
break;
}
}
} else {
// "dtv" argument is "type(t)", pass a raw pointer
auto *p{special
.GetProc<void (*)(const void *, int &, int &, char *, std::size_t)>(
bindings)};
for (; numElements-- > 0; descriptor.IncrementSubscripts(subscripts)) {
p(descriptor.Element<char>(subscripts), unit, ioStat, ioMsg,
sizeof ioMsg);
if (ioStat != IostatOk) {
break;
}
}
}
handler.Forward(ioStat, ioMsg, sizeof ioMsg);
external->PopChildIo(child);
return handler.GetIoStat() == IostatOk;
}
// Per-category descriptor-based I/O templates
// TODO (perhaps as a nontrivial but small starter project): implement
// automatic repetition counts, like "10*3.14159", for list-directed and
// NAMELIST array output.
template <int KIND, Direction DIR>
inline RT_API_ATTRS bool FormattedIntegerIO(IoStatementState &io,
const Descriptor &descriptor, [[maybe_unused]] bool isSigned) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
using IntType = CppTypeFor<common::TypeCategory::Integer, KIND>;
bool anyInput{false};
for (std::size_t j{0}; j < numElements; ++j) {
if (auto edit{io.GetNextDataEdit()}) {
IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
if constexpr (DIR == Direction::Output) {
if (!EditIntegerOutput<KIND>(io, *edit, x, isSigned)) {
return false;
}
} else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
if (EditIntegerInput(
io, *edit, reinterpret_cast<void *>(&x), KIND, isSigned)) {
anyInput = true;
} else {
return anyInput && edit->IsNamelist();
}
}
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
io.GetIoErrorHandler().Crash(
"FormattedIntegerIO: subscripts out of bounds");
}
} else {
return false;
}
}
return true;
}
template <int KIND, Direction DIR>
inline RT_API_ATTRS bool FormattedRealIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
bool anyInput{false};
for (std::size_t j{0}; j < numElements; ++j) {
if (auto edit{io.GetNextDataEdit()}) {
RawType &x{ExtractElement<RawType>(io, descriptor, subscripts)};
if constexpr (DIR == Direction::Output) {
if (!RealOutputEditing<KIND>{io, x}.Edit(*edit)) {
return false;
}
} else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
if (EditRealInput<KIND>(io, *edit, reinterpret_cast<void *>(&x))) {
anyInput = true;
} else {
return anyInput && edit->IsNamelist();
}
}
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
io.GetIoErrorHandler().Crash(
"FormattedRealIO: subscripts out of bounds");
}
} else {
return false;
}
}
return true;
}
template <int KIND, Direction DIR>
inline RT_API_ATTRS bool FormattedComplexIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
bool isListOutput{
io.get_if<ListDirectedStatementState<Direction::Output>>() != nullptr};
using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
bool anyInput{false};
for (std::size_t j{0}; j < numElements; ++j) {
RawType *x{&ExtractElement<RawType>(io, descriptor, subscripts)};
if (isListOutput) {
DataEdit rEdit, iEdit;
rEdit.descriptor = DataEdit::ListDirectedRealPart;
iEdit.descriptor = DataEdit::ListDirectedImaginaryPart;
rEdit.modes = iEdit.modes = io.mutableModes();
if (!RealOutputEditing<KIND>{io, x[0]}.Edit(rEdit) ||
!RealOutputEditing<KIND>{io, x[1]}.Edit(iEdit)) {
return false;
}
} else {
for (int k{0}; k < 2; ++k, ++x) {
auto edit{io.GetNextDataEdit()};
if (!edit) {
return false;
} else if constexpr (DIR == Direction::Output) {
if (!RealOutputEditing<KIND>{io, *x}.Edit(*edit)) {
return false;
}
} else if (edit->descriptor == DataEdit::ListDirectedNullValue) {
break;
} else if (EditRealInput<KIND>(
io, *edit, reinterpret_cast<void *>(x))) {
anyInput = true;
} else {
return anyInput && edit->IsNamelist();
}
}
}
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
io.GetIoErrorHandler().Crash(
"FormattedComplexIO: subscripts out of bounds");
}
}
return true;
}
template <typename A, Direction DIR>
inline RT_API_ATTRS bool FormattedCharacterIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
std::size_t length{descriptor.ElementBytes() / sizeof(A)};
auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
bool anyInput{false};
for (std::size_t j{0}; j < numElements; ++j) {
A *x{&ExtractElement<A>(io, descriptor, subscripts)};
if (listOutput) {
if (!ListDirectedCharacterOutput(io, *listOutput, x, length)) {
return false;
}
} else if (auto edit{io.GetNextDataEdit()}) {
if constexpr (DIR == Direction::Output) {
if (!EditCharacterOutput(io, *edit, x, length)) {
return false;
}
} else { // input
if (edit->descriptor != DataEdit::ListDirectedNullValue) {
if (EditCharacterInput(io, *edit, x, length)) {
anyInput = true;
} else {
return anyInput && edit->IsNamelist();
}
}
}
} else {
return false;
}
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
io.GetIoErrorHandler().Crash(
"FormattedCharacterIO: subscripts out of bounds");
}
}
return true;
}
template <int KIND, Direction DIR>
inline RT_API_ATTRS bool FormattedLogicalIO(
IoStatementState &io, const Descriptor &descriptor) {
std::size_t numElements{descriptor.Elements()};
SubscriptValue subscripts[maxRank];
descriptor.GetLowerBounds(subscripts);
auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
bool anyInput{false};
for (std::size_t j{0}; j < numElements; ++j) {
IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
if (listOutput) {
if (!ListDirectedLogicalOutput(io, *listOutput, x != 0)) {
return false;
}
} else if (auto edit{io.GetNextDataEdit()}) {
if constexpr (DIR == Direction::Output) {
if (!EditLogicalOutput(io, *edit, x != 0)) {
return false;
}
} else {
if (edit->descriptor != DataEdit::ListDirectedNullValue) {
bool truth{};
if (EditLogicalInput(io, *edit, truth)) {
x = truth;
anyInput = true;
} else {
return anyInput && edit->IsNamelist();
}
}
}
} else {
return false;
}
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
io.GetIoErrorHandler().Crash(
"FormattedLogicalIO: subscripts out of bounds");
}
}
return true;
}
template <Direction DIR>
RT_API_ATTRS int DerivedIoTicket<DIR>::Continue(WorkQueue &workQueue) {
while (!IsComplete()) {
if (component_->genre() == typeInfo::Component::Genre::Data) {
// Create a descriptor for the component
Descriptor &compDesc{componentDescriptor_.descriptor()};
component_->CreatePointerDescriptor(
compDesc, instance_, io_.GetIoErrorHandler(), subscripts_);
Advance();
if (int status{workQueue.BeginDescriptorIo<DIR>(
io_, compDesc, table_, anyIoTookPlace_)};
status != StatOk) {
return status;
}
} else {
// Component is itself a descriptor
char *pointer{
instance_.Element<char>(subscripts_) + component_->offset()};
const Descriptor &compDesc{
*reinterpret_cast<const Descriptor *>(pointer)};
Advance();
if (compDesc.IsAllocated()) {
if (int status{workQueue.BeginDescriptorIo<DIR>(
io_, compDesc, table_, anyIoTookPlace_)};
status != StatOk) {
return status;
}
}
}
}
return StatOk;
}
template RT_API_ATTRS int DerivedIoTicket<Direction::Output>::Continue(
WorkQueue &);
template RT_API_ATTRS int DerivedIoTicket<Direction::Input>::Continue(
WorkQueue &);
template <Direction DIR>
RT_API_ATTRS int DescriptorIoTicket<DIR>::Begin(WorkQueue &workQueue) {
IoErrorHandler &handler{io_.GetIoErrorHandler()};
if (handler.InError()) {
return handler.GetIoStat();
}
if (!io_.get_if<IoDirectionState<DIR>>()) {
handler.Crash("DescriptorIO() called for wrong I/O direction");
return handler.GetIoStat();
}
if constexpr (DIR == Direction::Input) {
if (!io_.BeginReadingRecord()) {
return StatOk;
}
}
if (!io_.get_if<FormattedIoStatementState<DIR>>()) {
// Unformatted I/O
IoErrorHandler &handler{io_.GetIoErrorHandler()};
const DescriptorAddendum *addendum{instance_.Addendum()};
if (const typeInfo::DerivedType *type{
addendum ? addendum->derivedType() : nullptr}) {
// derived type unformatted I/O
if (DIR == Direction::Input || !io_.get_if<InquireIOLengthState>()) {
if (table_) {
if (const auto *definedIo{table_->Find(*type,
DIR == Direction::Input
? common::DefinedIo::ReadUnformatted
: common::DefinedIo::WriteUnformatted)}) {
if (definedIo->subroutine) {
std::uint8_t isArgDescriptorSet{0};
if (definedIo->flags & IsDtvArgPolymorphic) {
isArgDescriptorSet = 1;
}
typeInfo::SpecialBinding special{DIR == Direction::Input
? typeInfo::SpecialBinding::Which::ReadUnformatted
: typeInfo::SpecialBinding::Which::WriteUnformatted,
definedIo->subroutine, isArgDescriptorSet,
/*IsTypeBound=*/false,
/*specialCaseFlag=*/!!(definedIo->flags & DefinedIoInteger8)};
if (DefinedUnformattedIo(io_, instance_, *type, special)) {
anyIoTookPlace_ = true;
return StatOk;
}
} else {
int status{workQueue.BeginDerivedIo<DIR>(
io_, instance_, *type, table_, anyIoTookPlace_)};
return status == StatContinue ? StatOk : status; // done here
}
}
}
if (const typeInfo::SpecialBinding *special{
type->FindSpecialBinding(DIR == Direction::Input
? typeInfo::SpecialBinding::Which::ReadUnformatted
: typeInfo::SpecialBinding::Which::WriteUnformatted)}) {
if (!table_ || !table_->ignoreNonTbpEntries ||
special->IsTypeBound()) {
// defined derived type unformatted I/O
if (DefinedUnformattedIo(io_, instance_, *type, *special)) {
anyIoTookPlace_ = true;
return StatOk;
} else {
return IostatEnd;
}
}
}
}
// Default derived type unformatted I/O
// TODO: If no component at any level has defined READ or WRITE
// (as appropriate), the elements are contiguous, and no byte swapping
// is active, do a block transfer via the code below.
int status{workQueue.BeginDerivedIo<DIR>(
io_, instance_, *type, table_, anyIoTookPlace_)};
return status == StatContinue ? StatOk : status; // done here
} else {
// intrinsic type unformatted I/O
auto *externalUnf{io_.get_if<ExternalUnformattedIoStatementState<DIR>>()};
ChildUnformattedIoStatementState<DIR> *childUnf{nullptr};
InquireIOLengthState *inq{nullptr};
bool swapEndianness{false};
if (externalUnf) {
swapEndianness = externalUnf->unit().swapEndianness();
} else {
childUnf = io_.get_if<ChildUnformattedIoStatementState<DIR>>();
if (!childUnf) {
inq = DIR == Direction::Output ? io_.get_if<InquireIOLengthState>()
: nullptr;
RUNTIME_CHECK(handler, inq != nullptr);
}
}
std::size_t elementBytes{instance_.ElementBytes()};
std::size_t swappingBytes{elementBytes};
if (auto maybeCatAndKind{instance_.type().GetCategoryAndKind()}) {
// Byte swapping units can be smaller than elements, namely
// for COMPLEX and CHARACTER.
if (maybeCatAndKind->first == TypeCategory::Character) {
// swap each character position independently
swappingBytes = maybeCatAndKind->second; // kind
} else if (maybeCatAndKind->first == TypeCategory::Complex) {
// swap real and imaginary components independently
swappingBytes /= 2;
}
}
using CharType =
std::conditional_t<DIR == Direction::Output, const char, char>;
auto Transfer{[=](CharType &x, std::size_t totalBytes) -> bool {
if constexpr (DIR == Direction::Output) {
return externalUnf ? externalUnf->Emit(&x, totalBytes, swappingBytes)
: childUnf ? childUnf->Emit(&x, totalBytes, swappingBytes)
: inq->Emit(&x, totalBytes, swappingBytes);
} else {
return externalUnf
? externalUnf->Receive(&x, totalBytes, swappingBytes)
: childUnf->Receive(&x, totalBytes, swappingBytes);
}
}};
if (!swapEndianness &&
instance_.IsContiguous()) { // contiguous unformatted I/O
char &x{ExtractElement<char>(io_, instance_, subscripts_)};
if (Transfer(x, elements_ * elementBytes)) {
anyIoTookPlace_ = true;
} else {
return IostatEnd;
}
} else { // non-contiguous or byte-swapped intrinsic type unformatted I/O
for (; !IsComplete(); Advance()) {
char &x{ExtractElement<char>(io_, instance_, subscripts_)};
if (Transfer(x, elementBytes)) {
anyIoTookPlace_ = true;
} else {
return IostatEnd;
}
}
}
}
// Unformatted I/O never needs to call Continue().
return StatOk;
}
// Formatted I/O
if (auto catAndKind{instance_.type().GetCategoryAndKind()}) {
TypeCategory cat{catAndKind->first};
int kind{catAndKind->second};
bool any{false};
switch (cat) {
case TypeCategory::Integer:
switch (kind) {
case 1:
any = FormattedIntegerIO<1, DIR>(io_, instance_, true);
break;
case 2:
any = FormattedIntegerIO<2, DIR>(io_, instance_, true);
break;
case 4:
any = FormattedIntegerIO<4, DIR>(io_, instance_, true);
break;
case 8:
any = FormattedIntegerIO<8, DIR>(io_, instance_, true);
break;
case 16:
any = FormattedIntegerIO<16, DIR>(io_, instance_, true);
break;
default:
handler.Crash(
"not yet implemented: INTEGER(KIND=%d) in formatted IO", kind);
return IostatEnd;
}
break;
case TypeCategory::Unsigned:
switch (kind) {
case 1:
any = FormattedIntegerIO<1, DIR>(io_, instance_, false);
break;
case 2:
any = FormattedIntegerIO<2, DIR>(io_, instance_, false);
break;
case 4:
any = FormattedIntegerIO<4, DIR>(io_, instance_, false);
break;
case 8:
any = FormattedIntegerIO<8, DIR>(io_, instance_, false);
break;
case 16:
any = FormattedIntegerIO<16, DIR>(io_, instance_, false);
break;
default:
handler.Crash(
"not yet implemented: UNSIGNED(KIND=%d) in formatted IO", kind);
return IostatEnd;
}
break;
case TypeCategory::Real:
switch (kind) {
case 2:
any = FormattedRealIO<2, DIR>(io_, instance_);
break;
case 3:
any = FormattedRealIO<3, DIR>(io_, instance_);
break;
case 4:
any = FormattedRealIO<4, DIR>(io_, instance_);
break;
case 8:
any = FormattedRealIO<8, DIR>(io_, instance_);
break;
case 10:
any = FormattedRealIO<10, DIR>(io_, instance_);
break;
// TODO: case double/double
case 16:
any = FormattedRealIO<16, DIR>(io_, instance_);
break;
default:
handler.Crash(
"not yet implemented: REAL(KIND=%d) in formatted IO", kind);
return IostatEnd;
}
break;
case TypeCategory::Complex:
switch (kind) {
case 2:
any = FormattedComplexIO<2, DIR>(io_, instance_);
break;
case 3:
any = FormattedComplexIO<3, DIR>(io_, instance_);
break;
case 4:
any = FormattedComplexIO<4, DIR>(io_, instance_);
break;
case 8:
any = FormattedComplexIO<8, DIR>(io_, instance_);
break;
case 10:
any = FormattedComplexIO<10, DIR>(io_, instance_);
break;
// TODO: case double/double
case 16:
any = FormattedComplexIO<16, DIR>(io_, instance_);
break;
default:
handler.Crash(
"not yet implemented: COMPLEX(KIND=%d) in formatted IO", kind);
return IostatEnd;
}
break;
case TypeCategory::Character:
switch (kind) {
case 1:
any = FormattedCharacterIO<char, DIR>(io_, instance_);
break;
case 2:
any = FormattedCharacterIO<char16_t, DIR>(io_, instance_);
break;
case 4:
any = FormattedCharacterIO<char32_t, DIR>(io_, instance_);
break;
default:
handler.Crash(
"not yet implemented: CHARACTER(KIND=%d) in formatted IO", kind);
return IostatEnd;
}
break;
case TypeCategory::Logical:
switch (kind) {
case 1:
any = FormattedLogicalIO<1, DIR>(io_, instance_);
break;
case 2:
any = FormattedLogicalIO<2, DIR>(io_, instance_);
break;
case 4:
any = FormattedLogicalIO<4, DIR>(io_, instance_);
break;
case 8:
any = FormattedLogicalIO<8, DIR>(io_, instance_);
break;
default:
handler.Crash(
"not yet implemented: LOGICAL(KIND=%d) in formatted IO", kind);
return IostatEnd;
}
break;
case TypeCategory::Derived: {
// Derived type information must be present for formatted I/O.
IoErrorHandler &handler{io_.GetIoErrorHandler()};
const DescriptorAddendum *addendum{instance_.Addendum()};
RUNTIME_CHECK(handler, addendum != nullptr);
derived_ = addendum->derivedType();
RUNTIME_CHECK(handler, derived_ != nullptr);
if (table_) {
if (const auto *definedIo{table_->Find(*derived_,
DIR == Direction::Input ? common::DefinedIo::ReadFormatted
: common::DefinedIo::WriteFormatted)}) {
if (definedIo->subroutine) {
nonTbpSpecial_.emplace(DIR == Direction::Input
? typeInfo::SpecialBinding::Which::ReadFormatted
: typeInfo::SpecialBinding::Which::WriteFormatted,
definedIo->subroutine,
/*isArgDescriptorSet=*/
(definedIo->flags & IsDtvArgPolymorphic) ? 1 : 0,
/*isTypeBound=*/false,
/*specialCaseFlag=*/!!(definedIo->flags & DefinedIoInteger8));
special_ = &*nonTbpSpecial_;
}
}
}
if (!special_) {
if (const typeInfo::SpecialBinding *binding{
derived_->FindSpecialBinding(DIR == Direction::Input
? typeInfo::SpecialBinding::Which::ReadFormatted
: typeInfo::SpecialBinding::Which::WriteFormatted)}) {
if (!table_ || !table_->ignoreNonTbpEntries ||
binding->IsTypeBound()) {
special_ = binding;
}
}
}
return StatContinue;
}
}
if (any) {
anyIoTookPlace_ = true;
} else {
return IostatEnd;
}
} else {
handler.Crash("DescriptorIO: bad type code (%d) in descriptor",
static_cast<int>(instance_.type().raw()));
return handler.GetIoStat();
}
return StatOk;
}
template RT_API_ATTRS int DescriptorIoTicket<Direction::Output>::Begin(
WorkQueue &);
template RT_API_ATTRS int DescriptorIoTicket<Direction::Input>::Begin(
WorkQueue &);
template <Direction DIR>
RT_API_ATTRS int DescriptorIoTicket<DIR>::Continue(WorkQueue &workQueue) {
// Only derived type formatted I/O gets here.
while (!IsComplete()) {
if (special_) {
if (auto defined{DefinedFormattedIo(
io_, instance_, *derived_, *special_, subscripts_)}) {
anyIoTookPlace_ |= *defined;
Advance();
continue;
}
}
Descriptor &elementDesc{elementDescriptor_.descriptor()};
elementDesc.Establish(
*derived_, nullptr, 0, nullptr, CFI_attribute_pointer);
elementDesc.set_base_addr(instance_.Element<char>(subscripts_));
Advance();
if (int status{workQueue.BeginDerivedIo<DIR>(
io_, elementDesc, *derived_, table_, anyIoTookPlace_)};
status != StatOk) {
return status;
}
}
return StatOk;
}
template RT_API_ATTRS int DescriptorIoTicket<Direction::Output>::Continue(
WorkQueue &);
template RT_API_ATTRS int DescriptorIoTicket<Direction::Input>::Continue(
WorkQueue &);
template <Direction DIR>
RT_API_ATTRS bool DescriptorIO(IoStatementState &io,
const Descriptor &descriptor, const NonTbpDefinedIoTable *originalTable) {
bool anyIoTookPlace{false};
const NonTbpDefinedIoTable *defaultTable{io.nonTbpDefinedIoTable()};
const NonTbpDefinedIoTable *table{originalTable};
if (!table) {
table = defaultTable;
} else if (table != defaultTable) {
io.set_nonTbpDefinedIoTable(table); // for nested I/O
}
WorkQueue workQueue{io.GetIoErrorHandler()};
if (workQueue.BeginDescriptorIo<DIR>(io, descriptor, table, anyIoTookPlace) ==
StatContinue) {
workQueue.Run();
}
if (defaultTable != table) {
io.set_nonTbpDefinedIoTable(defaultTable);
}
return anyIoTookPlace;
}
template RT_API_ATTRS bool DescriptorIO<Direction::Output>(
IoStatementState &, const Descriptor &, const NonTbpDefinedIoTable *);
template RT_API_ATTRS bool DescriptorIO<Direction::Input>(
IoStatementState &, const Descriptor &, const NonTbpDefinedIoTable *);
RT_OFFLOAD_API_GROUP_END
} // namespace Fortran::runtime::io::descr