//===-- runtime/allocatable.cpp -------------------------------------------===//
//
// 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 "flang/Runtime/allocatable.h"
#include "assign-impl.h"
#include "derived.h"
#include "stat.h"
#include "terminator.h"
#include "type-info.h"
#include "flang/ISO_Fortran_binding_wrapper.h"
#include "flang/Runtime/assign.h"
#include "flang/Runtime/descriptor.h"
namespace Fortran::runtime {
extern "C" {
RT_EXT_API_GROUP_BEGIN
void RTDEF(AllocatableInitIntrinsic)(Descriptor &descriptor,
TypeCategory category, int kind, int rank, int corank) {
INTERNAL_CHECK(corank == 0);
descriptor.Establish(TypeCode{category, kind},
Descriptor::BytesFor(category, kind), nullptr, rank, nullptr,
CFI_attribute_allocatable);
}
void RTDEF(AllocatableInitCharacter)(Descriptor &descriptor,
SubscriptValue length, int kind, int rank, int corank) {
INTERNAL_CHECK(corank == 0);
descriptor.Establish(
kind, length, nullptr, rank, nullptr, CFI_attribute_allocatable);
}
void RTDEF(AllocatableInitDerived)(Descriptor &descriptor,
const typeInfo::DerivedType &derivedType, int rank, int corank) {
INTERNAL_CHECK(corank == 0);
descriptor.Establish(
derivedType, nullptr, rank, nullptr, CFI_attribute_allocatable);
}
void RTDEF(AllocatableInitIntrinsicForAllocate)(Descriptor &descriptor,
TypeCategory category, int kind, int rank, int corank) {
if (!descriptor.IsAllocated()) {
RTNAME(AllocatableInitIntrinsic)(descriptor, category, kind, rank, corank);
}
}
void RTDEF(AllocatableInitCharacterForAllocate)(Descriptor &descriptor,
SubscriptValue length, int kind, int rank, int corank) {
if (!descriptor.IsAllocated()) {
RTNAME(AllocatableInitCharacter)(descriptor, length, kind, rank, corank);
}
}
void RTDEF(AllocatableInitDerivedForAllocate)(Descriptor &descriptor,
const typeInfo::DerivedType &derivedType, int rank, int corank) {
if (!descriptor.IsAllocated()) {
RTNAME(AllocatableInitDerived)(descriptor, derivedType, rank, corank);
}
}
std::int32_t RTDEF(MoveAlloc)(Descriptor &to, Descriptor &from,
const typeInfo::DerivedType *derivedType, bool hasStat,
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
// If to and from are the same allocatable they must not be allocated
// and nothing should be done.
if (from.raw().base_addr == to.raw().base_addr && from.IsAllocated()) {
return ReturnError(
terminator, StatMoveAllocSameAllocatable, errMsg, hasStat);
}
if (to.IsAllocated()) {
int stat{
to.Destroy(/*finalize=*/true, /*destroyPointers=*/false, &terminator)};
if (stat != StatOk) {
return ReturnError(terminator, stat, errMsg, hasStat);
}
}
// If from isn't allocated, the standard defines that nothing should be done.
if (from.IsAllocated()) {
to = from;
from.raw().base_addr = nullptr;
// Carry over the dynamic type.
if (auto *toAddendum{to.Addendum()}) {
if (const auto *fromAddendum{from.Addendum()}) {
if (const auto *derived{fromAddendum->derivedType()}) {
toAddendum->set_derivedType(derived);
}
}
}
// Reset from dynamic type if needed.
if (auto *fromAddendum{from.Addendum()}) {
if (derivedType) {
fromAddendum->set_derivedType(derivedType);
}
}
}
return StatOk;
}
void RTDEF(AllocatableSetBounds)(Descriptor &descriptor, int zeroBasedDim,
SubscriptValue lower, SubscriptValue upper) {
INTERNAL_CHECK(zeroBasedDim >= 0 && zeroBasedDim < descriptor.rank());
if (descriptor.IsAllocatable() && !descriptor.IsAllocated()) {
descriptor.GetDimension(zeroBasedDim).SetBounds(lower, upper);
// The byte strides are computed when the object is allocated.
}
}
void RTDEF(AllocatableSetDerivedLength)(
Descriptor &descriptor, int which, SubscriptValue x) {
if (descriptor.IsAllocatable() && !descriptor.IsAllocated()) {
DescriptorAddendum *addendum{descriptor.Addendum()};
INTERNAL_CHECK(addendum != nullptr);
addendum->SetLenParameterValue(which, x);
}
}
void RTDEF(AllocatableApplyMold)(
Descriptor &descriptor, const Descriptor &mold, int rank) {
if (descriptor.IsAllocatable() && !descriptor.IsAllocated()) {
descriptor.ApplyMold(mold, rank);
}
}
int RTDEF(AllocatableAllocate)(Descriptor &descriptor, bool hasStat,
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
if (!descriptor.IsAllocatable()) {
return ReturnError(terminator, StatInvalidDescriptor, errMsg, hasStat);
} else if (descriptor.IsAllocated()) {
return ReturnError(terminator, StatBaseNotNull, errMsg, hasStat);
} else {
int stat{ReturnError(terminator, descriptor.Allocate(), errMsg, hasStat)};
if (stat == StatOk) {
if (const DescriptorAddendum * addendum{descriptor.Addendum()}) {
if (const auto *derived{addendum->derivedType()}) {
if (!derived->noInitializationNeeded()) {
stat =
Initialize(descriptor, *derived, terminator, hasStat, errMsg);
}
}
}
}
return stat;
}
}
int RTDEF(AllocatableAllocateSource)(Descriptor &alloc,
const Descriptor &source, bool hasStat, const Descriptor *errMsg,
const char *sourceFile, int sourceLine) {
int stat{RTNAME(AllocatableAllocate)(
alloc, hasStat, errMsg, sourceFile, sourceLine)};
if (stat == StatOk) {
Terminator terminator{sourceFile, sourceLine};
DoFromSourceAssign(alloc, source, terminator);
}
return stat;
}
int RTDEF(AllocatableDeallocate)(Descriptor &descriptor, bool hasStat,
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
if (!descriptor.IsAllocatable()) {
return ReturnError(terminator, StatInvalidDescriptor, errMsg, hasStat);
} else if (!descriptor.IsAllocated()) {
return ReturnError(terminator, StatBaseNull, errMsg, hasStat);
} else {
return ReturnError(terminator,
descriptor.Destroy(
/*finalize=*/true, /*destroyPointers=*/false, &terminator),
errMsg, hasStat);
}
}
int RTDEF(AllocatableDeallocatePolymorphic)(Descriptor &descriptor,
const typeInfo::DerivedType *derivedType, bool hasStat,
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
int stat{RTNAME(AllocatableDeallocate)(
descriptor, hasStat, errMsg, sourceFile, sourceLine)};
if (stat == StatOk) {
if (DescriptorAddendum * addendum{descriptor.Addendum()}) {
addendum->set_derivedType(derivedType);
descriptor.raw().type = derivedType ? CFI_type_struct : CFI_type_other;
} else {
// Unlimited polymorphic descriptors initialized with
// AllocatableInitIntrinsic do not have an addendum. Make sure the
// derivedType is null in that case.
INTERNAL_CHECK(!derivedType);
descriptor.raw().type = CFI_type_other;
}
}
return stat;
}
void RTDEF(AllocatableDeallocateNoFinal)(
Descriptor &descriptor, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
if (!descriptor.IsAllocatable()) {
ReturnError(terminator, StatInvalidDescriptor);
} else if (!descriptor.IsAllocated()) {
ReturnError(terminator, StatBaseNull);
} else {
ReturnError(terminator,
descriptor.Destroy(
/*finalize=*/false, /*destroyPointers=*/false, &terminator));
}
}
// TODO: AllocatableCheckLengthParameter
RT_EXT_API_GROUP_END
}
} // namespace Fortran::runtime