1//===-- flang/unittests/Runtime/ArrayConstructor.cpp-------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "gtest/gtest.h"
10#include "tools.h"
11#include "flang/Runtime/allocatable.h"
12#include "flang/Runtime/array-constructor.h"
13#include "flang/Runtime/cpp-type.h"
14#include "flang/Runtime/descriptor.h"
15#include "flang/Runtime/type-code.h"
16
17#include <memory>
18
19using namespace Fortran::runtime;
20using Fortran::common::TypeCategory;
21
22TEST(ArrayConstructor, Basic) {
23 // X(4) = [1,2,3,4]
24 // Y(2:3,4:6) = RESHAPE([5,6,7,8,9,10], shape=[2,3])
25 //
26 // Test creation of: [(i, X, Y, i=0,99,1)]
27 auto x{MakeArray<TypeCategory::Integer, 4>(
28 std::vector<int>{4}, std::vector<std::int32_t>{1, 2, 3, 4})};
29 auto y{MakeArray<TypeCategory::Integer, 4>(
30 std::vector<int>{2, 3}, std::vector<std::int32_t>{5, 6, 7, 8, 9, 10})};
31 y->GetDimension(0).SetBounds(2, 3);
32 y->GetDimension(1).SetBounds(4, 6);
33
34 StaticDescriptor<1, false> statDesc;
35 Descriptor &result{statDesc.descriptor()};
36 result.Establish(TypeCode{CFI_type_int32_t}, 4, /*p=*/nullptr,
37 /*rank=*/1, /*extents=*/nullptr, CFI_attribute_allocatable);
38 std::allocator<ArrayConstructorVector> cookieAllocator;
39 ArrayConstructorVector *acVector{cookieAllocator.allocate(1)};
40 ASSERT_TRUE(acVector);
41
42 // Case 1: result is a temp and extent is unknown before first call.
43 result.GetDimension(0).SetBounds(1, 0);
44
45 RTNAME(InitArrayConstructorVector)
46 (*acVector, result, /*useValueLengthParameters=*/false,
47 /*vectorClassSize=*/sizeof(ArrayConstructorVector));
48 for (std::int32_t i{0}; i <= 99; ++i) {
49 RTNAME(PushArrayConstructorSimpleScalar)(*acVector, &i);
50 RTNAME(PushArrayConstructorValue)(*acVector, *x);
51 RTNAME(PushArrayConstructorValue)(*acVector, *y);
52 }
53
54 ASSERT_TRUE(result.IsAllocated());
55 ASSERT_EQ(result.Elements(), static_cast<std::size_t>(100 * (1 + 4 + 2 * 3)));
56 SubscriptValue subscript[1]{1};
57 for (std::int32_t i{0}; i <= 99; ++i) {
58 ASSERT_EQ(*result.Element<std::int32_t>(subscript), i);
59 ++subscript[0];
60 for (std::int32_t j{1}; j <= 10; ++j) {
61 EXPECT_EQ(*result.Element<std::int32_t>(subscript), j);
62 ++subscript[0];
63 }
64 }
65 EXPECT_LE(result.Elements(),
66 static_cast<std::size_t>(acVector->actualAllocationSize));
67 result.Deallocate();
68 ASSERT_TRUE(!result.IsAllocated());
69
70 // Case 2: result is an unallocated temp and extent is know before first call.
71 // and is allocated when the first value is pushed.
72 result.GetDimension(0).SetBounds(1, 1234);
73 RTNAME(InitArrayConstructorVector)
74 (*acVector, result, /*useValueLengthParameters=*/false,
75 /*vectorClassSize=*/sizeof(ArrayConstructorVector));
76 EXPECT_EQ(0, acVector->actualAllocationSize);
77 std::int32_t i{42};
78 RTNAME(PushArrayConstructorSimpleScalar)(*acVector, &i);
79 ASSERT_TRUE(result.IsAllocated());
80 EXPECT_EQ(1234, acVector->actualAllocationSize);
81 result.Deallocate();
82
83 cookieAllocator.deallocate(acVector, 1);
84}
85
86TEST(ArrayConstructor, Character) {
87 // CHARACTER(2) :: C = "12"
88 // X(4) = ["ab", "cd", "ef", "gh"]
89 // Y(2:3,4:6) = RESHAPE(["ij", "jl", "mn", "op", "qr","st"], shape=[2,3])
90 auto x{MakeArray<TypeCategory::Character, 1>(std::vector<int>{4},
91 std::vector<std::string>{"ab", "cd", "ef", "gh"}, 2)};
92 auto y{MakeArray<TypeCategory::Character, 1>(std::vector<int>{2, 3},
93 std::vector<std::string>{"ij", "kl", "mn", "op", "qr", "st"}, 2)};
94 y->GetDimension(0).SetBounds(2, 3);
95 y->GetDimension(1).SetBounds(4, 6);
96 auto c{MakeArray<TypeCategory::Character, 1>(
97 std::vector<int>{}, std::vector<std::string>{"12"}, 2)};
98
99 StaticDescriptor<1, false> statDesc;
100 Descriptor &result{statDesc.descriptor()};
101 result.Establish(TypeCode{CFI_type_char}, 0, /*p=*/nullptr,
102 /*rank=*/1, /*extents=*/nullptr, CFI_attribute_allocatable);
103 std::allocator<ArrayConstructorVector> cookieAllocator;
104 ArrayConstructorVector *acVector{cookieAllocator.allocate(1)};
105 ASSERT_TRUE(acVector);
106
107 // Case 1: result is a temp and extent and length are unknown before the first
108 // call. Test creation of: [(C, X, Y, i=1,10,1)]
109 static constexpr std::size_t expectedElements{10 * (1 + 4 + 2 * 3)};
110 result.GetDimension(0).SetBounds(1, 0);
111 RTNAME(InitArrayConstructorVector)
112 (*acVector, result, /*useValueLengthParameters=*/true,
113 /*vectorClassSize=*/sizeof(ArrayConstructorVector));
114 for (std::int32_t i{1}; i <= 10; ++i) {
115 RTNAME(PushArrayConstructorValue)(*acVector, *c);
116 RTNAME(PushArrayConstructorValue)(*acVector, *x);
117 RTNAME(PushArrayConstructorValue)(*acVector, *y);
118 }
119 ASSERT_TRUE(result.IsAllocated());
120 ASSERT_EQ(result.Elements(), expectedElements);
121 ASSERT_EQ(result.ElementBytes(), 2u);
122 EXPECT_LE(result.Elements(),
123 static_cast<std::size_t>(acVector->actualAllocationSize));
124 std::string CXY{"12abcdefghijklmnopqrst"};
125 std::string expect;
126 for (int i{0}; i < 10; ++i)
127 expect.append(str: CXY);
128 EXPECT_EQ(std::memcmp(
129 result.OffsetElement<char>(0), expect.data(), expect.length()),
130 0);
131 result.Deallocate();
132 cookieAllocator.deallocate(acVector, 1);
133}
134
135TEST(ArrayConstructor, CharacterRuntimeCheck) {
136 // CHARACTER(2) :: C2
137 // CHARACTER(3) :: C3
138 // Test the runtime catch bad [C2, C3] array constructors (Fortran 2018 7.8
139 // point 2.)
140 auto c2{MakeArray<TypeCategory::Character, 1>(
141 std::vector<int>{}, std::vector<std::string>{"ab"}, 2)};
142 auto c3{MakeArray<TypeCategory::Character, 1>(
143 std::vector<int>{}, std::vector<std::string>{"abc"}, 3)};
144 StaticDescriptor<1, false> statDesc;
145 Descriptor &result{statDesc.descriptor()};
146 result.Establish(TypeCode{CFI_type_char}, 0, /*p=*/nullptr,
147 /*rank=*/1, /*extents=*/nullptr, CFI_attribute_allocatable);
148 std::allocator<ArrayConstructorVector> cookieAllocator;
149 ArrayConstructorVector *acVector{cookieAllocator.allocate(1)};
150 ASSERT_TRUE(acVector);
151
152 result.GetDimension(0).SetBounds(1, 0);
153 RTNAME(InitArrayConstructorVector)
154 (*acVector, result, /*useValueLengthParameters=*/true,
155 /*vectorClassSize=*/sizeof(ArrayConstructorVector));
156 RTNAME(PushArrayConstructorValue)(*acVector, *c2);
157 ASSERT_DEATH(RTNAME(PushArrayConstructorValue)(*acVector, *c3),
158 "Array constructor: mismatched character lengths");
159}
160

source code of flang/unittests/Runtime/ArrayConstructor.cpp