[flang] test and debug RESHAPE

Original-commit: flang-compiler/f18@c20ce350c1
Reviewed-on: https://github.com/flang-compiler/f18/pull/162
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-08-02 17:04:31 -07:00
parent a8fed82258
commit fac96c4612
8 changed files with 244 additions and 113 deletions

View file

@ -16,7 +16,6 @@
// as specified in section 18.5.5 of Fortran 2018.
#include "descriptor.h"
#include <cstdlib>
namespace Fortran::ISO {
extern "C" {
@ -37,12 +36,13 @@ int CFI_allocate(CFI_cdesc_t *descriptor, const CFI_index_t lower_bounds[],
if (descriptor->version != CFI_VERSION) {
return CFI_INVALID_DESCRIPTOR;
}
if ((descriptor->attribute &
~(CFI_attribute_pointer | CFI_attribute_allocatable)) != 0) {
if (descriptor->attribute != CFI_attribute_allocatable &&
descriptor->attribute != CFI_attribute_pointer) {
// Non-interoperable object
return CFI_INVALID_DESCRIPTOR;
}
if (descriptor->base_addr != nullptr) {
if (descriptor->attribute == CFI_attribute_allocatable &&
descriptor->base_addr != nullptr) {
return CFI_ERROR_BASE_ADDR_NOT_NULL;
}
if (descriptor->rank > CFI_MAX_RANK) {
@ -70,7 +70,7 @@ int CFI_allocate(CFI_cdesc_t *descriptor, const CFI_index_t lower_bounds[],
dim->sm = byteSize;
byteSize *= extent;
}
void *p{std::malloc(byteSize)};
void *p{new char[byteSize]};
if (p == nullptr) {
return CFI_ERROR_MEM_ALLOCATION;
}
@ -83,15 +83,15 @@ int CFI_deallocate(CFI_cdesc_t *descriptor) {
if (descriptor->version != CFI_VERSION) {
return CFI_INVALID_DESCRIPTOR;
}
if ((descriptor->attribute &
~(CFI_attribute_pointer | CFI_attribute_allocatable)) != 0) {
if (descriptor->attribute != CFI_attribute_allocatable &&
descriptor->attribute != CFI_attribute_pointer) {
// Non-interoperable object
return CFI_INVALID_DESCRIPTOR;
}
if (descriptor->base_addr == nullptr) {
return CFI_ERROR_BASE_ADDR_NULL;
}
std::free(descriptor->base_addr);
delete[] static_cast<char *>(descriptor->base_addr);
descriptor->base_addr = nullptr;
return CFI_SUCCESS;
}
@ -141,12 +141,16 @@ static constexpr std::size_t MinElemLen(CFI_type_t type) {
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[]) {
if ((attribute & ~(CFI_attribute_pointer | CFI_attribute_allocatable)) != 0) {
if (attribute != CFI_attribute_other && attribute != CFI_attribute_pointer &&
attribute != CFI_attribute_allocatable) {
return CFI_INVALID_ATTRIBUTE;
}
if (rank > CFI_MAX_RANK) {
return CFI_INVALID_RANK;
}
if (base_addr != nullptr && attribute != CFI_attribute_pointer) {
return CFI_ERROR_BASE_ADDR_NOT_NULL;
}
if (rank > 0 && base_addr != nullptr && extents == nullptr) {
return CFI_INVALID_EXTENT;
}
@ -177,7 +181,14 @@ int CFI_establish(CFI_cdesc_t *descriptor, void *base_addr,
}
int CFI_is_contiguous(const CFI_cdesc_t *descriptor) {
return 0; // TODO
std::size_t bytes{descriptor->elem_len};
for (int j{0}; j < descriptor->rank; ++j) {
if (bytes != descriptor->dim[j].sm) {
return 0;
}
bytes *= descriptor->dim[j].extent;
}
return 1;
}
int CFI_section(CFI_cdesc_t *result, const CFI_cdesc_t *source,

View file

@ -47,4 +47,36 @@ bool DerivedType::IsNontrivialAnalysis() const {
}
return false;
}
void DerivedType::Initialize(char *instance) const {
if (typeBoundProcedures_ > InitializerTBP) {
if (auto f{reinterpret_cast<void (*)(char *)>(
typeBoundProcedure_[InitializerTBP].code.host)}) {
f(instance);
}
}
for (std::size_t j{0}; j < components_; ++j) {
if (const Descriptor * descriptor{component_[j].GetDescriptor(instance)}) {
// TODO
}
}
}
void DerivedType::Destroy(char *instance, bool finalize) const {
if (finalize && typeBoundProcedures_ > FinalTBP) {
if (auto f{reinterpret_cast<void (*)(char *)>(
typeBoundProcedure_[FinalTBP].code.host)}) {
f(instance);
}
}
const char *constInstance{instance};
for (std::size_t j{0}; j < components_; ++j) {
if (Descriptor * descriptor{component_[j].GetDescriptor(instance)}) {
descriptor->Deallocate(finalize);
} else if (const Descriptor *
descriptor{component_[j].GetDescriptor(constInstance)}) {
descriptor->Destroy(component_[j].Locate<char>(instance), finalize);
}
}
}
} // namespace Fortran::runtime

View file

@ -77,6 +77,14 @@ public:
return reinterpret_cast<const A *>(dtInstance + offset_);
}
Descriptor *GetDescriptor(char *dtInstance) const {
if (IsDescriptor()) {
return Locate<Descriptor>(dtInstance);
} else {
return nullptr;
}
}
const Descriptor *GetDescriptor(const char *dtInstance) const {
if (staticDescriptor_ != nullptr) {
return staticDescriptor_;
@ -144,14 +152,8 @@ public:
std::size_t components() const { return components_; }
// TBP 0 is the initializer: SUBROUTINE INIT(INSTANCE)
static constexpr int initializerTBP{0};
// TBP 1 is the sourced allocation copier: SUBROUTINE COPYINIT(TO, FROM)
static constexpr int copierTBP{1};
// TBP 2 is the FINAL subroutine.
static constexpr int finalTBP{2};
// The first few type-bound procedure indices are special.
enum SpecialTBP { InitializerTBP, CopierTBP, FinalTBP };
std::size_t typeBoundProcedures() const { return typeBoundProcedures_; }
const TypeBoundProcedure &typeBoundProcedure(int n) const {
@ -176,6 +178,9 @@ public:
bool IsSameType(const DerivedType &) const;
void Initialize(char *instance) const;
void Destroy(char *instance, bool finalize = true) const;
private:
enum Flag { SEQUENCE = 1, BIND_C = 2, NONTRIVIAL = 4 };

View file

@ -20,10 +20,9 @@
namespace Fortran::runtime {
Descriptor::~Descriptor() {
// Descriptors created by Descriptor::Create() must be destroyed by
// Descriptor::Destroy(), not by the default destructor, so that
// the array variant operator delete[] is properly used.
assert(!(Addendum() && (Addendum()->flags() & DescriptorAddendum::Created)));
if (raw_.attribute != CFI_attribute_pointer) {
Deallocate();
}
}
void Descriptor::Establish(TypeCode t, std::size_t elementBytes, void *p,
@ -60,42 +59,33 @@ void Descriptor::Establish(const DerivedType &dt, void *p, int rank,
new (Addendum()) DescriptorAddendum{&dt};
}
Descriptor *Descriptor::Create(TypeCode t, std::size_t elementBytes, void *p,
int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) {
std::unique_ptr<Descriptor> Descriptor::Create(TypeCode t,
std::size_t elementBytes, void *p, int rank, const SubscriptValue *extent,
ISO::CFI_attribute_t attribute) {
std::size_t bytes{SizeInBytes(rank, true)};
Descriptor *result{reinterpret_cast<Descriptor *>(std::malloc(bytes))};
Descriptor *result{reinterpret_cast<Descriptor *>(new char[bytes])};
CHECK(result != nullptr);
result->Establish(t, elementBytes, p, rank, extent, attribute, true);
result->Addendum()->flags() |= DescriptorAddendum::Created;
return result;
return std::unique_ptr<Descriptor>{result};
}
Descriptor *Descriptor::Create(TypeCategory c, int kind, void *p, int rank,
const SubscriptValue *extent, ISO::CFI_attribute_t attribute) {
std::unique_ptr<Descriptor> Descriptor::Create(TypeCategory c, int kind,
void *p, int rank, const SubscriptValue *extent,
ISO::CFI_attribute_t attribute) {
std::size_t bytes{SizeInBytes(rank, true)};
Descriptor *result{reinterpret_cast<Descriptor *>(std::malloc(bytes))};
Descriptor *result{reinterpret_cast<Descriptor *>(new char[bytes])};
CHECK(result != nullptr);
result->Establish(c, kind, p, rank, extent, attribute, true);
result->Addendum()->flags() |= DescriptorAddendum::Created;
return result;
return std::unique_ptr<Descriptor>{result};
}
Descriptor *Descriptor::Create(const DerivedType &dt, void *p, int rank,
const SubscriptValue *extent, ISO::CFI_attribute_t attribute) {
std::unique_ptr<Descriptor> Descriptor::Create(const DerivedType &dt, void *p,
int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) {
std::size_t bytes{SizeInBytes(rank, true, dt.lenParameters())};
Descriptor *result{reinterpret_cast<Descriptor *>(std::malloc(bytes))};
Descriptor *result{reinterpret_cast<Descriptor *>(new char[bytes])};
CHECK(result != nullptr);
result->Establish(dt, p, rank, extent, attribute);
result->Addendum()->flags() |= DescriptorAddendum::Created;
return result;
}
void Descriptor::Destroy() {
if (const DescriptorAddendum * addendum{Addendum()}) {
if (addendum->flags() & DescriptorAddendum::Created) {
std::free(reinterpret_cast<void *>(this));
}
}
return std::unique_ptr<Descriptor>{result};
}
std::size_t Descriptor::SizeInBytes() const {
@ -113,11 +103,75 @@ std::size_t Descriptor::Elements() const {
return elements;
}
int Descriptor::Allocate(
const SubscriptValue lb[], const SubscriptValue ub[], std::size_t charLen) {
int result{ISO::CFI_allocate(&raw_, lb, ub, charLen)};
if (result == CFI_SUCCESS) {
// TODO: derived type initialization
}
return result;
}
int Descriptor::Deallocate(bool finalize) {
if (raw_.base_addr != nullptr) {
Destroy(static_cast<char *>(raw_.base_addr), finalize);
}
return ISO::CFI_deallocate(&raw_);
}
void Descriptor::Destroy(char *data, bool finalize) const {
if (data != nullptr) {
if (const DescriptorAddendum * addendum{Addendum()}) {
if (addendum->flags() & DescriptorAddendum::DoNotFinalize) {
finalize = false;
}
if (const DerivedType * dt{addendum->derivedType()}) {
std::size_t elements{Elements()};
std::size_t elementBytes{ElementBytes()};
for (std::size_t j{0}; j < elements; ++j) {
dt->Destroy(data + j * elementBytes, finalize);
}
}
}
}
}
void Descriptor::Check() const {
// TODO
}
std::ostream &Descriptor::Dump(std::ostream &o) const {
o << "Descriptor @ 0x" << std::hex << reinterpret_cast<std::intptr_t>(this)
<< std::dec << ":\n";
o << " base_addr 0x" << std::hex
<< reinterpret_cast<std::intptr_t>(raw_.base_addr) << std::dec << '\n';
o << " elem_len " << raw_.elem_len << '\n';
o << " version " << raw_.version
<< (raw_.version == CFI_VERSION ? "(ok)" : "BAD!") << '\n';
o << " rank " << static_cast<int>(raw_.rank) << '\n';
o << " type " << static_cast<int>(raw_.type) << '\n';
o << " attribute " << static_cast<int>(raw_.attribute) << '\n';
o << " addendum? " << static_cast<bool>(raw_.f18Addendum) << '\n';
for (int j{0}; j < raw_.rank; ++j) {
o << " dim[" << j << "] lower_bound " << raw_.dim[j].lower_bound << '\n';
o << " extent " << raw_.dim[j].extent << '\n';
o << " sm " << raw_.dim[j].sm << '\n';
}
if (const DescriptorAddendum * addendum{Addendum()}) {
addendum->Dump(o);
}
return o;
}
std::size_t DescriptorAddendum::SizeInBytes() const {
return SizeInBytes(LenParameters());
}
std::ostream &DescriptorAddendum::Dump(std::ostream &o) const {
o << " derivedType @ 0x" << std::hex
<< reinterpret_cast<std::intptr_t>(derivedType_) << std::dec << '\n';
o << " flags " << flags_ << '\n';
// TODO: LEN parameter values
return o;
}
} // namespace Fortran::runtime

View file

@ -31,6 +31,8 @@
#include <cinttypes>
#include <cstddef>
#include <cstring>
#include <memory>
#include <ostream>
namespace Fortran::runtime {
@ -63,11 +65,8 @@ public:
enum Flags {
StaticDescriptor = 0x001,
ImplicitAllocatable = 0x002, // compiler-created allocatable
Created = 0x004, // allocated by Descriptor::Create()
DoNotFinalize = 0x008, // compiler temporary
Target = 0x010, // TARGET attribute
AllContiguous = 0x020, // all array elements are contiguous
LeadingDimensionContiguous = 0x040, // only leading dimension contiguous
DoNotFinalize = 0x004, // compiler temporary
Target = 0x008, // TARGET attribute
};
explicit DescriptorAddendum(
@ -100,6 +99,8 @@ public:
len_[which] = x;
}
std::ostream &Dump(std::ostream &) const;
private:
const DerivedType *derivedType_{nullptr};
std::uint64_t flags_{0};
@ -116,13 +117,20 @@ public:
// Be advised: this class type is not suitable for use when allocating
// a descriptor -- it is a dynamic view of the common descriptor format.
// If used in a simple declaration of a local variable or dynamic allocation,
// the size is going to be wrong, since the true size of a descriptor
// depends on the number of its dimensions and the presence of an addendum.
// the size is going to be correct only by accident, since the true size of
// a descriptor depends on the number of its dimensions and the presence and
// size of an addendum, which depends on the type of the data.
// Use the class template StaticDescriptor (below) to declare a descriptor
// whose type and rank are fixed and known at compilation time. Use the
// Create() static member functions otherwise to dynamically allocate a
// descriptor.
Descriptor() = delete;
Descriptor() {
// Minimal initialization to prevent the destructor from running amuck
// later if the descriptor is never established.
raw_.base_addr = nullptr;
raw_.f18Addendum = false;
}
~Descriptor();
@ -138,20 +146,18 @@ public:
const SubscriptValue *extent = nullptr,
ISO::CFI_attribute_t attribute = CFI_attribute_other);
static Descriptor *Create(TypeCode t, std::size_t elementBytes,
static std::unique_ptr<Descriptor> Create(TypeCode t,
std::size_t elementBytes, void *p = nullptr, int rank = maxRank,
const SubscriptValue *extent = nullptr,
ISO::CFI_attribute_t attribute = CFI_attribute_other);
static std::unique_ptr<Descriptor> Create(TypeCategory, int kind,
void *p = nullptr, int rank = maxRank,
const SubscriptValue *extent = nullptr,
ISO::CFI_attribute_t attribute = CFI_attribute_other);
static Descriptor *Create(TypeCategory, int kind, void *p = nullptr,
int rank = maxRank, const SubscriptValue *extent = nullptr,
static std::unique_ptr<Descriptor> Create(const DerivedType &dt,
void *p = nullptr, int rank = maxRank,
const SubscriptValue *extent = nullptr,
ISO::CFI_attribute_t attribute = CFI_attribute_other);
static Descriptor *Create(const DerivedType &dt, void *p = nullptr,
int rank = maxRank, const SubscriptValue *extent = nullptr,
ISO::CFI_attribute_t attribute = CFI_attribute_other);
// Descriptor instances allocated via Create() above must be deallocated
// by calling Destroy().
void Destroy();
ISO::CFI_cdesc_t &raw() { return raw_; }
const ISO::CFI_cdesc_t &raw() const { return raw_; }
@ -168,6 +174,7 @@ public:
bool IsAllocatable() const {
return raw_.attribute == CFI_attribute_allocatable;
}
bool IsAllocated() const { return raw_.base_addr != nullptr; }
Dimension &GetDimension(int dim) {
return *reinterpret_cast<Dimension *>(&raw_.dim[dim]);
@ -247,20 +254,29 @@ public:
std::size_t Elements() const;
bool IsContiguous() const {
if (raw_.attribute == CFI_attribute_allocatable) {
return true;
int Allocate(const SubscriptValue lb[], const SubscriptValue ub[],
std::size_t charLen = 0); // TODO: SOURCE= and MOLD=
int Deallocate(bool finalize = true);
void Destroy(char *data, bool finalize = true) const;
bool IsContiguous(int leadingDimensions = maxRank) const {
auto bytes{static_cast<SubscriptValue>(ElementBytes())};
for (int j{0}; j < leadingDimensions && j < raw_.rank; ++j) {
const Dimension &dim{GetDimension(j)};
if (bytes != dim.ByteStride()) {
return false;
}
bytes *= dim.Extent();
}
if (const DescriptorAddendum * addendum{Addendum()}) {
return (addendum->flags() & DescriptorAddendum::AllContiguous) != 0;
}
return false;
return true;
}
void Check() const;
// TODO: creation of array sections
std::ostream &Dump(std::ostream &) const;
private:
ISO::CFI_cdesc_t raw_;
};
@ -284,6 +300,10 @@ public:
static constexpr std::size_t byteSize{
Descriptor::SizeInBytes(maxRank, hasAddendum, maxLengthTypeParameters)};
StaticDescriptor() { new (storage_) Descriptor{}; }
~StaticDescriptor() { descriptor().~Descriptor(); }
Descriptor &descriptor() { return *reinterpret_cast<Descriptor *>(storage_); }
const Descriptor &descriptor() const {
return *reinterpret_cast<const Descriptor *>(storage_);

View file

@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "descriptor.h"
#include "transformational.h"
#include "../lib/common/idioms.h"
#include "../lib/evaluate/integer.h"
#include <algorithm>
#include <bitset>
#include <cinttypes>
#include <cstdlib>
#include <memory>
namespace Fortran::runtime {
@ -39,8 +39,8 @@ static inline std::int64_t GetInt64(const char *p, std::size_t bytes) {
}
// F2018 16.9.163
Descriptor *RESHAPE(const Descriptor &source, const Descriptor &shape,
const Descriptor *pad, const Descriptor *order) {
std::unique_ptr<Descriptor> RESHAPE(const Descriptor &source,
const Descriptor &shape, const Descriptor *pad, const Descriptor *order) {
// Compute and check the rank of the result.
CHECK(shape.rank() == 1);
CHECK(shape.type().IsInteger());
@ -48,11 +48,13 @@ Descriptor *RESHAPE(const Descriptor &source, const Descriptor &shape,
CHECK(resultRank >= 0 && resultRank <= static_cast<SubscriptValue>(maxRank));
// Extract and check the shape of the result; compute its element count.
SubscriptValue lowerBound[maxRank]; // all 1's
SubscriptValue resultExtent[maxRank];
std::size_t shapeElementBytes{shape.ElementBytes()};
std::size_t resultElements{1};
SubscriptValue shapeSubscript{shape.GetDimension(0).LowerBound()};
for (SubscriptValue j{0}; j < resultRank; ++j, ++shapeSubscript) {
lowerBound[j] = 1;
resultExtent[j] =
GetInt64(shape.Element<char>(&shapeSubscript), shapeElementBytes);
CHECK(resultExtent[j] >= 0);
@ -61,11 +63,12 @@ Descriptor *RESHAPE(const Descriptor &source, const Descriptor &shape,
// Check that there are sufficient elements in the SOURCE=, or that
// the optional PAD= argument is present and nonempty.
std::size_t elementBytes{source.ElementBytes()};
std::size_t sourceElements{source.Elements()};
std::size_t padElements{pad ? pad->Elements() : 0};
if (resultElements < sourceElements) {
CHECK(padElements > 0);
CHECK(pad->ElementBytes() == source.ElementBytes());
CHECK(pad->ElementBytes() == elementBytes);
}
// Extract and check the optional ORDER= argument, which must be a
@ -89,28 +92,22 @@ Descriptor *RESHAPE(const Descriptor &source, const Descriptor &shape,
}
}
// Allocate the result's data storage.
std::size_t elementBytes{source.ElementBytes()};
std::size_t resultBytes{resultElements * elementBytes};
void *data{std::malloc(resultBytes)};
CHECK(resultBytes == 0 || data != nullptr);
// Create and populate the result's descriptor.
const DescriptorAddendum *sourceAddendum{source.Addendum()};
const DerivedType *sourceDerivedType{
sourceAddendum ? sourceAddendum->derivedType() : nullptr};
Descriptor *result{nullptr};
std::unique_ptr<Descriptor> result;
if (sourceDerivedType != nullptr) {
result =
Descriptor::Create(*sourceDerivedType, data, resultRank, resultExtent);
result = Descriptor::Create(*sourceDerivedType, nullptr, resultRank,
resultExtent, CFI_attribute_allocatable);
} else {
result = Descriptor::Create(
source.type(), elementBytes, data, resultRank, resultExtent);
result = Descriptor::Create(source.type(), elementBytes, nullptr,
resultRank, resultExtent,
CFI_attribute_allocatable); // TODO rearrange these arguments
}
DescriptorAddendum *resultAddendum{result->Addendum()};
CHECK(resultAddendum != nullptr);
resultAddendum->flags() |= DescriptorAddendum::DoNotFinalize;
resultAddendum->flags() |= DescriptorAddendum::AllContiguous;
if (sourceDerivedType != nullptr) {
std::size_t lenParameters{sourceDerivedType->lenParameters()};
for (std::size_t j{0}; j < lenParameters; ++j) {
@ -118,6 +115,11 @@ Descriptor *RESHAPE(const Descriptor &source, const Descriptor &shape,
j, sourceAddendum->LenParameterValue(j));
}
}
// Allocate storage for the result's data.
int status{result->Allocate(lowerBound, resultExtent, elementBytes)};
if (status != CFI_SUCCESS) {
common::die("RESHAPE: Allocate failed (error %d)", status);
}
// Populate the result's elements.
SubscriptValue resultSubscript[maxRank];

View file

@ -16,11 +16,13 @@
#define FORTRAN_RUNTIME_TRANSFORMATIONAL_H_
#include "descriptor.h"
#include <memory>
namespace Fortran::runtime {
Descriptor *RESHAPE(const Descriptor &source, const Descriptor &shape,
const Descriptor *pad = nullptr, const Descriptor *order = nullptr);
std::unique_ptr<Descriptor> RESHAPE(const Descriptor &source,
const Descriptor &shape, const Descriptor *pad = nullptr,
const Descriptor *order = nullptr);
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TRANSFORMATIONAL_H_

View file

@ -21,44 +21,56 @@ using namespace Fortran::common;
using namespace Fortran::runtime;
int main() {
std::size_t dataElements{24};
std::int32_t *data{new std::int32_t[dataElements]};
for (std::size_t j{0}; j < dataElements; ++j) {
data[j] = j;
}
static const SubscriptValue ones[]{1, 1, 1};
static const SubscriptValue sourceExtent[]{2, 3, 4};
Descriptor *source{Descriptor::Create(TypeCategory::Integer, sizeof data[0],
reinterpret_cast<void *>(data), 3, sourceExtent,
CFI_attribute_allocatable)};
std::unique_ptr<Descriptor> source{
Descriptor::Create(TypeCategory::Integer, sizeof(std::int32_t), nullptr,
3, sourceExtent, CFI_attribute_allocatable)};
source->Check();
MATCH(3, source->rank());
MATCH(sizeof(std::int32_t), source->ElementBytes());
TEST(source->IsAllocatable());
TEST(!source->IsPointer());
TEST(source->Allocate(ones, sourceExtent, sizeof(std::int32_t)) ==
CFI_SUCCESS);
TEST(source->IsAllocated());
MATCH(2, source->GetDimension(0).Extent());
MATCH(3, source->GetDimension(1).Extent());
MATCH(4, source->GetDimension(2).Extent());
MATCH(24, source->Elements());
for (std::size_t j{0}; j < 24; ++j) {
*source->Element<std::int32_t>(j * sizeof(std::int32_t)) = j;
}
static const std::int16_t shapeData[]{8, 4};
static const SubscriptValue shapeExtent{2};
Descriptor *shape{Descriptor::Create(TypeCategory::Integer,
std::unique_ptr<Descriptor> shape{Descriptor::Create(TypeCategory::Integer,
static_cast<int>(sizeof shapeData[0]),
const_cast<void *>(reinterpret_cast<const void *>(shapeData)), 1,
&shapeExtent)};
&shapeExtent, CFI_attribute_pointer)};
shape->Check();
MATCH(1, shape->rank());
MATCH(2, shape->GetDimension(0).Extent());
TEST(shape->IsPointer());
TEST(!shape->IsAllocatable());
StaticDescriptor<3> padDescriptor;
Descriptor &pad{padDescriptor.descriptor()};
static const std::int32_t padData[]{24, 25, 26, 27, 28, 29, 30, 31};
static const SubscriptValue padExtent[]{2, 2, 3};
padDescriptor.descriptor().Establish(TypeCategory::Integer,
static_cast<int>(sizeof padData[0]),
const_cast<void *>(reinterpret_cast<const void *>(padData)), 3,
padExtent);
pad.Establish(TypeCategory::Integer, static_cast<int>(sizeof padData[0]),
const_cast<void *>(reinterpret_cast<const void *>(padData)), 3, padExtent,
CFI_attribute_pointer);
padDescriptor.Check();
pad.Check();
MATCH(3, pad.rank());
MATCH(2, pad.GetDimension(0).Extent());
MATCH(2, pad.GetDimension(1).Extent());
MATCH(3, pad.GetDimension(2).Extent());
Descriptor *result{RESHAPE(*source, *shape, &padDescriptor.descriptor())};
std::unique_ptr<Descriptor> result{RESHAPE(*source, *shape, &pad)};
TEST(result != nullptr);
TEST(result.get() != nullptr);
result->Check();
MATCH(sizeof(std::int32_t), result->ElementBytes());
MATCH(2, result->rank());
@ -73,12 +85,5 @@ int main() {
// TODO: test ORDER=
// Plug leaks; should run cleanly beneath valgrind
free(result->raw().base_addr);
result->Destroy();
shape->Destroy();
source->Destroy();
delete[] data;
return testing::Complete();
}