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 19 using namespace Fortran::runtime; 20 using Fortran::common::TypeCategory; 21 22 TEST(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 for (std::int32_t i{0}; i <= 99; ++i) { 48 RTNAME(PushArrayConstructorSimpleScalar)(*acVector, &i); 49 RTNAME(PushArrayConstructorValue)(*acVector, *x); 50 RTNAME(PushArrayConstructorValue)(*acVector, *y); 51 } 52 53 ASSERT_TRUE(result.IsAllocated()); 54 ASSERT_EQ(result.Elements(), static_cast<std::size_t>(100 * (1 + 4 + 2 * 3))); 55 SubscriptValue subscript[1]{1}; 56 for (std::int32_t i{0}; i <= 99; ++i) { 57 ASSERT_EQ(*result.Element<std::int32_t>(subscript), i); 58 ++subscript[0]; 59 for (std::int32_t j{1}; j <= 10; ++j) { 60 EXPECT_EQ(*result.Element<std::int32_t>(subscript), j); 61 ++subscript[0]; 62 } 63 } 64 EXPECT_LE(result.Elements(), 65 static_cast<std::size_t>(acVector->actualAllocationSize)); 66 result.Deallocate(); 67 ASSERT_TRUE(!result.IsAllocated()); 68 69 // Case 2: result is an unallocated temp and extent is know before first call. 70 // and is allocated when the first value is pushed. 71 result.GetDimension(0).SetBounds(1, 1234); 72 RTNAME(InitArrayConstructorVector) 73 (*acVector, result, /*useValueLengthParameters=*/false); 74 EXPECT_EQ(0, acVector->actualAllocationSize); 75 std::int32_t i{42}; 76 RTNAME(PushArrayConstructorSimpleScalar)(*acVector, &i); 77 ASSERT_TRUE(result.IsAllocated()); 78 EXPECT_EQ(1234, acVector->actualAllocationSize); 79 result.Deallocate(); 80 81 cookieAllocator.deallocate(acVector, 1); 82 } 83 84 TEST(ArrayConstructor, Character) { 85 // CHARACTER(2) :: C = "12" 86 // X(4) = ["ab", "cd", "ef", "gh"] 87 // Y(2:3,4:6) = RESHAPE(["ij", "jl", "mn", "op", "qr","st"], shape=[2,3]) 88 auto x{MakeArray<TypeCategory::Character, 1>(std::vector<int>{4}, 89 std::vector<std::string>{"ab", "cd", "ef", "gh"}, 2)}; 90 auto y{MakeArray<TypeCategory::Character, 1>(std::vector<int>{2, 3}, 91 std::vector<std::string>{"ij", "kl", "mn", "op", "qr", "st"}, 2)}; 92 y->GetDimension(0).SetBounds(2, 3); 93 y->GetDimension(1).SetBounds(4, 6); 94 auto c{MakeArray<TypeCategory::Character, 1>( 95 std::vector<int>{}, std::vector<std::string>{"12"}, 2)}; 96 97 StaticDescriptor<1, false> statDesc; 98 Descriptor &result{statDesc.descriptor()}; 99 result.Establish(TypeCode{CFI_type_char}, 0, /*p=*/nullptr, 100 /*rank=*/1, /*extents=*/nullptr, CFI_attribute_allocatable); 101 std::allocator<ArrayConstructorVector> cookieAllocator; 102 ArrayConstructorVector *acVector{cookieAllocator.allocate(1)}; 103 ASSERT_TRUE(acVector); 104 105 // Case 1: result is a temp and extent and length are unknown before the first 106 // call. Test creation of: [(C, X, Y, i=1,10,1)] 107 static constexpr std::size_t expectedElements{10 * (1 + 4 + 2 * 3)}; 108 result.GetDimension(0).SetBounds(1, 0); 109 RTNAME(InitArrayConstructorVector) 110 (*acVector, result, /*useValueLengthParameters=*/true); 111 for (std::int32_t i{1}; i <= 10; ++i) { 112 RTNAME(PushArrayConstructorValue)(*acVector, *c); 113 RTNAME(PushArrayConstructorValue)(*acVector, *x); 114 RTNAME(PushArrayConstructorValue)(*acVector, *y); 115 } 116 ASSERT_TRUE(result.IsAllocated()); 117 ASSERT_EQ(result.Elements(), expectedElements); 118 ASSERT_EQ(result.ElementBytes(), 2u); 119 EXPECT_LE(result.Elements(), 120 static_cast<std::size_t>(acVector->actualAllocationSize)); 121 std::string CXY{"12abcdefghijklmnopqrst"}; 122 std::string expect; 123 for (int i{0}; i < 10; ++i) 124 expect.append(CXY); 125 EXPECT_EQ(std::memcmp( 126 result.OffsetElement<char>(0), expect.data(), expect.length()), 127 0); 128 result.Deallocate(); 129 cookieAllocator.deallocate(acVector, 1); 130 x->Deallocate(); 131 y->Deallocate(); 132 c->Deallocate(); 133 } 134 135 TEST(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 RTNAME(PushArrayConstructorValue)(*acVector, *c2); 156 ASSERT_DEATH(RTNAME(PushArrayConstructorValue)(*acVector, *c3), 157 "Array constructor: mismatched character lengths"); 158 } 159