xref: /llvm-project/flang/unittests/Evaluate/ISO-Fortran-binding.cpp (revision 32f901bdf9b59a1cf43946ac7bb6c9382bc69600)
1 #include "testing.h"
2 #include "../../include/flang/ISO_Fortran_binding.h"
3 #include "../../runtime/descriptor.h"
4 #include "llvm/Support/raw_ostream.h"
5 #include <type_traits>
6 
7 using namespace Fortran::runtime;
8 using namespace Fortran::ISO;
9 
10 // CFI_CDESC_T test helpers
11 template <int rank> class Test_CFI_CDESC_T {
12 public:
13   Test_CFI_CDESC_T() {}
14   ~Test_CFI_CDESC_T() {}
15   void Check() {
16     // Test CFI_CDESC_T macro defined in section 18.5.4 of F2018 standard
17     // CFI_CDESC_T must give storage that is:
18     using type = decltype(dvStorage_);
19     // unqualified
20     MATCH(false, std::is_const<type>::value);
21     MATCH(false, std::is_volatile<type>::value);
22     // suitable in size
23     if (rank > 0) {
24       MATCH(sizeof(dvStorage_), Descriptor::SizeInBytes(rank_, false));
25     } else { // C++ implementation over-allocates for rank=0 by 24bytes.
26       MATCH(true, sizeof(dvStorage_) >= Descriptor::SizeInBytes(rank_, false));
27     }
28     // suitable in alignment
29     MATCH(0,
30         reinterpret_cast<std::uintptr_t>(&dvStorage_) &
31             (alignof(CFI_cdesc_t) - 1));
32   }
33 
34 private:
35   static constexpr int rank_{rank};
36   CFI_CDESC_T(rank) dvStorage_;
37 };
38 
39 template <int rank> static void TestCdescMacroForAllRanksSmallerThan() {
40   static_assert(rank > 0, "rank<0!");
41   Test_CFI_CDESC_T<rank> obj;
42   obj.Check();
43   TestCdescMacroForAllRanksSmallerThan<rank - 1>();
44 }
45 
46 template <> void TestCdescMacroForAllRanksSmallerThan<0>() {
47   Test_CFI_CDESC_T<0> obj;
48   obj.Check();
49 }
50 
51 // CFI_establish test helper
52 static void AddNoiseToCdesc(CFI_cdesc_t *dv, CFI_rank_t rank) {
53   static const int trap{0};
54   dv->rank = 16;
55   // This address is not supposed to be used. Any write attempt should trigger
56   // program termination
57   dv->base_addr = const_cast<int *>(&trap);
58   dv->elem_len = 320;
59   dv->type = CFI_type_struct;
60   dv->attribute = CFI_attribute_pointer;
61   for (int i{0}; i < rank; i++) {
62     dv->dim[i].extent = -42;
63     dv->dim[i].lower_bound = -42;
64     dv->dim[i].sm = -42;
65   }
66 }
67 
68 #ifdef VERBOSE
69 static void DumpTestWorld(const void *bAddr, CFI_attribute_t attr,
70     CFI_type_t ty, std::size_t eLen, CFI_rank_t rank,
71     const CFI_index_t *eAddr) {
72   llvm::outs() << " base_addr: ";
73   llvm::outs().write_hex(reinterpret_cast<std::intptr_t>(bAddr))
74       << " attribute: " << static_cast<int>(attr)
75       << " type: " << static_cast<int>(ty) << " elem_len: " << eLen
76       << " rank: " << static_cast<int>(rank) << " extent: ";
77   llvm::outs().write_hex(reinterpret_cast<std::intptr_t>(eAddr)) << '\n';
78   llvm::outs().flush();
79 }
80 #endif
81 
82 static void check_CFI_establish(CFI_cdesc_t *dv, void *base_addr,
83     CFI_attribute_t attribute, CFI_type_t type, std::size_t elem_len,
84     CFI_rank_t rank, const CFI_index_t extents[]) {
85 #ifdef VERBOSE
86   DumpTestWorld(base_addr, attribute, type, elem_len, rank, extent);
87 #endif
88   // CFI_establish reqs from F2018 section 18.5.5
89   int retCode{
90       CFI_establish(dv, base_addr, attribute, type, elem_len, rank, extents)};
91   Descriptor *res{reinterpret_cast<Descriptor *>(dv)};
92   if (retCode == CFI_SUCCESS) {
93     res->Check();
94     MATCH((attribute == CFI_attribute_pointer), res->IsPointer());
95     MATCH((attribute == CFI_attribute_allocatable), res->IsAllocatable());
96     MATCH(rank, res->rank());
97     MATCH(reinterpret_cast<std::intptr_t>(dv->base_addr),
98         reinterpret_cast<std::intptr_t>(base_addr));
99     MATCH(true, dv->version == CFI_VERSION);
100     if (base_addr != nullptr) {
101       MATCH(true, res->IsContiguous());
102       for (int i{0}; i < rank; ++i) {
103         MATCH(extents[i], res->GetDimension(i).Extent());
104       }
105     }
106     if (attribute == CFI_attribute_allocatable) {
107       MATCH(res->IsAllocated(), false);
108     }
109     if (attribute == CFI_attribute_pointer) {
110       if (base_addr != nullptr) {
111         for (int i{0}; i < rank; ++i) {
112           MATCH(0, res->GetDimension(i).LowerBound());
113         }
114       }
115     }
116     if (type == CFI_type_struct || type == CFI_type_char ||
117         type == CFI_type_other) {
118       MATCH(elem_len, res->ElementBytes());
119     }
120   }
121   // Checking failure/success according to combination of args forbidden by the
122   // standard:
123   int numErr{0};
124   int expectedRetCode{CFI_SUCCESS};
125   if (base_addr != nullptr && attribute == CFI_attribute_allocatable) {
126     ++numErr;
127     expectedRetCode = CFI_ERROR_BASE_ADDR_NOT_NULL;
128   }
129   if (rank > CFI_MAX_RANK) {
130     ++numErr;
131     expectedRetCode = CFI_INVALID_RANK;
132   }
133   if (type < 0 || type > CFI_TYPE_LAST) {
134     ++numErr;
135     expectedRetCode = CFI_INVALID_TYPE;
136   }
137 
138   if ((type == CFI_type_struct || type == CFI_type_char ||
139           type == CFI_type_other) &&
140       elem_len <= 0) {
141     ++numErr;
142     expectedRetCode = CFI_INVALID_ELEM_LEN;
143   }
144   if (rank > 0 && base_addr != nullptr && extents == nullptr) {
145     ++numErr;
146     expectedRetCode = CFI_INVALID_EXTENT;
147   }
148   if (numErr > 1) {
149     MATCH(true, retCode != CFI_SUCCESS);
150   } else {
151     MATCH(retCode, expectedRetCode);
152   }
153 }
154 
155 static void run_CFI_establish_tests() {
156   // Testing CFI_establish defined in section 18.5.5
157   CFI_index_t extents[CFI_MAX_RANK];
158   for (int i{0}; i < CFI_MAX_RANK; ++i) {
159     extents[i] = i + 66;
160   }
161   CFI_CDESC_T(CFI_MAX_RANK) dv_storage;
162   CFI_cdesc_t *dv{&dv_storage};
163   char base;
164   void *dummyAddr{&base};
165   // Define test space
166   CFI_attribute_t attrCases[]{
167       CFI_attribute_pointer, CFI_attribute_allocatable, CFI_attribute_other};
168   CFI_type_t typeCases[]{CFI_type_int, CFI_type_struct, CFI_type_double,
169       CFI_type_char, CFI_type_other, CFI_TYPE_LAST + 1};
170   CFI_index_t *extentCases[]{extents, nullptr};
171   void *baseAddrCases[]{dummyAddr, nullptr};
172   CFI_rank_t rankCases[]{0, 1, CFI_MAX_RANK, CFI_MAX_RANK + 1};
173   std::size_t lenCases[]{0, 42};
174 
175   for (CFI_attribute_t attribute : attrCases) {
176     for (void *base_addr : baseAddrCases) {
177       for (CFI_index_t *extent : extentCases) {
178         for (CFI_rank_t rank : rankCases) {
179           for (CFI_type_t type : typeCases) {
180             for (size_t elem_len : lenCases) {
181               AddNoiseToCdesc(dv, CFI_MAX_RANK);
182               check_CFI_establish(
183                   dv, base_addr, attribute, type, elem_len, rank, extent);
184             }
185           }
186         }
187       }
188     }
189   }
190   // If base_addr is null, extents shall be ignored even if rank !=0
191   const int rank3d{3};
192   CFI_CDESC_T(rank3d) dv3darrayStorage;
193   CFI_cdesc_t *dv_3darray{&dv3darrayStorage};
194   AddNoiseToCdesc(dv_3darray, rank3d); // => dv_3darray->dim[2].extent = -42
195   check_CFI_establish(dv_3darray, nullptr, CFI_attribute_other, CFI_type_int, 4,
196       rank3d, extents);
197   MATCH(false,
198       dv_3darray->dim[2].extent == 2 + 66); // extents was read
199 }
200 
201 static void check_CFI_address(
202     const CFI_cdesc_t *dv, const CFI_index_t subscripts[]) {
203   // 18.5.5.2
204   void *addr{CFI_address(dv, subscripts)};
205   const Descriptor *desc{reinterpret_cast<const Descriptor *>(dv)};
206   void *addrCheck{desc->Element<void>(subscripts)};
207   MATCH(true, addr == addrCheck);
208 }
209 
210 // Helper function to set lower bound of descriptor
211 static void EstablishLowerBounds(CFI_cdesc_t *dv, CFI_index_t *sub) {
212   for (int i{0}; i < dv->rank; ++i) {
213     dv->dim[i].lower_bound = sub[i];
214   }
215 }
216 
217 // Helper to get size without making internal compiler functions accessible
218 static std::size_t ByteSize(CFI_type_t ty, std::size_t size) {
219   CFI_CDESC_T(0) storage;
220   CFI_cdesc_t *dv{&storage};
221   int retCode{
222       CFI_establish(dv, nullptr, CFI_attribute_other, ty, size, 0, nullptr)};
223   return retCode == CFI_SUCCESS ? dv->elem_len : 0;
224 }
225 
226 static void run_CFI_address_tests() {
227   // Test CFI_address defined in 18.5.5.2
228   // Create test world
229   CFI_index_t extents[CFI_MAX_RANK];
230   CFI_CDESC_T(CFI_MAX_RANK) dv_storage;
231   CFI_cdesc_t *dv{&dv_storage};
232   char base;
233   void *dummyAddr{&base};
234   CFI_attribute_t attrCases[]{
235       CFI_attribute_pointer, CFI_attribute_allocatable, CFI_attribute_other};
236   CFI_type_t validTypeCases[]{
237       CFI_type_int, CFI_type_struct, CFI_type_double, CFI_type_char};
238   CFI_index_t subscripts[CFI_MAX_RANK];
239   CFI_index_t negativeLowerBounds[CFI_MAX_RANK];
240   CFI_index_t zeroLowerBounds[CFI_MAX_RANK];
241   CFI_index_t positiveLowerBounds[CFI_MAX_RANK];
242   CFI_index_t *lowerBoundCases[]{
243       negativeLowerBounds, zeroLowerBounds, positiveLowerBounds};
244   for (int i{0}; i < CFI_MAX_RANK; ++i) {
245     negativeLowerBounds[i] = -1;
246     zeroLowerBounds[i] = 0;
247     positiveLowerBounds[i] = 1;
248     extents[i] = i + 2;
249     subscripts[i] = i + 1;
250   }
251 
252   // test for scalar
253   for (CFI_attribute_t attribute : attrCases) {
254     for (CFI_type_t type : validTypeCases) {
255       CFI_establish(dv, dummyAddr, attribute, type, 42, 0, nullptr);
256       check_CFI_address(dv, nullptr);
257     }
258   }
259   // test for arrays
260   CFI_establish(dv, dummyAddr, CFI_attribute_other, CFI_type_int, 0,
261       CFI_MAX_RANK, extents);
262   for (CFI_index_t *lowerBounds : lowerBoundCases) {
263     EstablishLowerBounds(dv, lowerBounds);
264     for (CFI_type_t type : validTypeCases) {
265       for (bool contiguous : {true, false}) {
266         std::size_t size{ByteSize(type, 12)};
267         dv->elem_len = size;
268         for (int i{0}; i < dv->rank; ++i) {
269           dv->dim[i].sm = size + (contiguous ? 0 : dv->elem_len);
270           size = dv->dim[i].sm * dv->dim[i].extent;
271         }
272         for (CFI_attribute_t attribute : attrCases) {
273           dv->attribute = attribute;
274           check_CFI_address(dv, subscripts);
275         }
276       }
277     }
278   }
279   // Test on an assumed size array.
280   CFI_establish(
281       dv, dummyAddr, CFI_attribute_other, CFI_type_int, 0, 3, extents);
282   dv->dim[2].extent = -1;
283   check_CFI_address(dv, subscripts);
284 }
285 
286 static void check_CFI_allocate(CFI_cdesc_t *dv,
287     const CFI_index_t lower_bounds[], const CFI_index_t upper_bounds[],
288     std::size_t elem_len) {
289   // 18.5.5.3
290   // Backup descriptor data for future checks
291   const CFI_rank_t rank{dv->rank};
292   const std::size_t desc_elem_len{dv->elem_len};
293   const CFI_attribute_t attribute{dv->attribute};
294   const CFI_type_t type{dv->type};
295   const void *base_addr{dv->base_addr};
296   const int version{dv->version};
297 #ifdef VERBOSE
298   DumpTestWorld(base_addr, attribute, type, elem_len, rank, nullptr);
299 #endif
300   int retCode{CFI_allocate(dv, lower_bounds, upper_bounds, elem_len)};
301   Descriptor *desc = reinterpret_cast<Descriptor *>(dv);
302   if (retCode == CFI_SUCCESS) {
303     // check res properties from 18.5.5.3 par 3
304     MATCH(true, dv->base_addr != nullptr);
305     for (int i{0}; i < rank; ++i) {
306       MATCH(lower_bounds[i], dv->dim[i].lower_bound);
307       MATCH(upper_bounds[i], dv->dim[i].extent + dv->dim[i].lower_bound - 1);
308     }
309     if (type == CFI_type_char) {
310       MATCH(elem_len, dv->elem_len);
311     } else {
312       MATCH(true, desc_elem_len == dv->elem_len);
313     }
314     MATCH(true, desc->IsContiguous());
315   } else {
316     MATCH(true, base_addr == dv->base_addr);
317   }
318 
319   // Below dv members shall not be altered by CFI_allocate regardless of
320   // success/failure
321   MATCH(true, attribute == dv->attribute);
322   MATCH(true, rank == dv->rank);
323   MATCH(true, type == dv->type);
324   MATCH(true, version == dv->version);
325 
326   // Success/failure according to standard
327   int numErr{0};
328   int expectedRetCode{CFI_SUCCESS};
329   if (rank > CFI_MAX_RANK) {
330     ++numErr;
331     expectedRetCode = CFI_INVALID_RANK;
332   }
333   if (type < 0 || type > CFI_TYPE_LAST) {
334     ++numErr;
335     expectedRetCode = CFI_INVALID_TYPE;
336   }
337   if (base_addr != nullptr && attribute == CFI_attribute_allocatable) {
338     // This is less restrictive than 18.5.5.3 arg req for which pointers arg
339     // shall be unassociated. However, this match ALLOCATE behavior
340     // (9.7.3/9.7.4)
341     ++numErr;
342     expectedRetCode = CFI_ERROR_BASE_ADDR_NOT_NULL;
343   }
344   if (attribute != CFI_attribute_pointer &&
345       attribute != CFI_attribute_allocatable) {
346     ++numErr;
347     expectedRetCode = CFI_INVALID_ATTRIBUTE;
348   }
349   if (rank > 0 && (lower_bounds == nullptr || upper_bounds == nullptr)) {
350     ++numErr;
351     expectedRetCode = CFI_INVALID_EXTENT;
352   }
353 
354   // Memory allocation failures are unpredictable in this test.
355   if (numErr == 0 && retCode != CFI_SUCCESS) {
356     MATCH(true, retCode == CFI_ERROR_MEM_ALLOCATION);
357   } else if (numErr > 1) {
358     MATCH(true, retCode != CFI_SUCCESS);
359   } else {
360     MATCH(expectedRetCode, retCode);
361   }
362   // clean-up
363   if (retCode == CFI_SUCCESS) {
364     CFI_deallocate(dv);
365   }
366 }
367 
368 static void run_CFI_allocate_tests() {
369   // 18.5.5.3
370   // create test world
371   CFI_CDESC_T(CFI_MAX_RANK) dv_storage;
372   CFI_cdesc_t *dv{&dv_storage};
373   char base;
374   void *dummyAddr{&base};
375   CFI_attribute_t attrCases[]{
376       CFI_attribute_pointer, CFI_attribute_allocatable, CFI_attribute_other};
377   CFI_type_t typeCases[]{CFI_type_int, CFI_type_struct, CFI_type_double,
378       CFI_type_char, CFI_type_other, CFI_TYPE_LAST + 1};
379   void *baseAddrCases[]{dummyAddr, nullptr};
380   CFI_rank_t rankCases[]{0, 1, CFI_MAX_RANK, CFI_MAX_RANK + 1};
381   std::size_t lenCases[]{0, 42};
382   CFI_index_t lb1[CFI_MAX_RANK];
383   CFI_index_t ub1[CFI_MAX_RANK];
384   for (int i{0}; i < CFI_MAX_RANK; ++i) {
385     lb1[i] = -1;
386     ub1[i] = 0;
387   }
388 
389   check_CFI_establish(
390       dv, nullptr, CFI_attribute_other, CFI_type_int, 0, 0, nullptr);
391   for (CFI_type_t type : typeCases) {
392     std::size_t ty_len{ByteSize(type, 12)};
393     for (CFI_attribute_t attribute : attrCases) {
394       for (void *base_addr : baseAddrCases) {
395         for (CFI_rank_t rank : rankCases) {
396           for (size_t elem_len : lenCases) {
397             dv->base_addr = base_addr;
398             dv->rank = rank;
399             dv->attribute = attribute;
400             dv->type = type;
401             dv->elem_len = ty_len;
402             check_CFI_allocate(dv, lb1, ub1, elem_len);
403           }
404         }
405       }
406     }
407   }
408 }
409 
410 static void run_CFI_section_tests() {
411   // simple tests
412   bool testPreConditions{true};
413   constexpr CFI_index_t m{5}, n{6}, o{7};
414   constexpr CFI_rank_t rank{3};
415   long long array[o][n][m]; // Fortran A(m,n,o)
416   long long counter{1};
417 
418   for (CFI_index_t k{0}; k < o; ++k) {
419     for (CFI_index_t j{0}; j < n; ++j) {
420       for (CFI_index_t i{0}; i < m; ++i) {
421         array[k][j][i] = counter++; // Fortran A(i,j,k)
422       }
423     }
424   }
425   CFI_CDESC_T(rank) sourceStorage;
426   CFI_cdesc_t *source{&sourceStorage};
427   CFI_index_t extent[rank] = {m, n, o};
428   int retCode{CFI_establish(source, &array, CFI_attribute_other,
429       CFI_type_long_long, 0, rank, extent)};
430   testPreConditions &= (retCode == CFI_SUCCESS);
431 
432   CFI_index_t lb[rank] = {2, 5, 4};
433   CFI_index_t ub[rank] = {4, 5, 6};
434   CFI_index_t strides[rank] = {2, 0, 2};
435   constexpr CFI_rank_t resultRank{rank - 1};
436 
437   CFI_CDESC_T(resultRank) resultStorage;
438   CFI_cdesc_t *result{&resultStorage};
439   retCode = CFI_establish(result, nullptr, CFI_attribute_other,
440       CFI_type_long_long, 0, resultRank, nullptr);
441   testPreConditions &= (retCode == CFI_SUCCESS);
442 
443   if (!testPreConditions) {
444     MATCH(true, testPreConditions);
445     return;
446   }
447 
448   retCode = CFI_section(
449       result, source, lb, ub, strides); // Fortran B = A(2:4:2, 5:5:0, 4:6:2)
450   MATCH(true, retCode == CFI_SUCCESS);
451 
452   const CFI_index_t lbs0{source->dim[0].lower_bound};
453   const CFI_index_t lbs1{source->dim[1].lower_bound};
454   const CFI_index_t lbs2{source->dim[2].lower_bound};
455 
456   CFI_index_t resJ{result->dim[1].lower_bound};
457   for (CFI_index_t k{lb[2]}; k <= ub[2]; k += strides[2]) {
458     for (CFI_index_t j{lb[1]}; j <= ub[1]; j += strides[1] ? strides[1] : 1) {
459       CFI_index_t resI{result->dim[0].lower_bound};
460       for (CFI_index_t i{lb[0]}; i <= ub[0]; i += strides[0]) {
461         // check A(i,j,k) == B(resI, resJ) == array[k-1][j-1][i-1]
462         const CFI_index_t resSubcripts[]{resI, resJ};
463         const CFI_index_t srcSubcripts[]{i, j, k};
464         MATCH(true,
465             CFI_address(source, srcSubcripts) ==
466                 CFI_address(result, resSubcripts));
467         MATCH(true,
468             CFI_address(source, srcSubcripts) ==
469                 &array[k - lbs2][j - lbs1][i - lbs0]);
470         ++resI;
471       }
472     }
473     ++resJ;
474   }
475 
476   strides[0] = -1;
477   lb[0] = 4;
478   ub[0] = 2;
479   retCode = CFI_section(
480       result, source, lb, ub, strides); // Fortran B = A(4:2:-1, 5:5:0, 4:6:2)
481   MATCH(true, retCode == CFI_SUCCESS);
482 
483   resJ = result->dim[1].lower_bound;
484   for (CFI_index_t k{lb[2]}; k <= ub[2]; k += strides[2]) {
485     for (CFI_index_t j{lb[1]}; j <= ub[1]; j += 1) {
486       CFI_index_t resI{result->dim[1].lower_bound + result->dim[0].extent - 1};
487       for (CFI_index_t i{2}; i <= 4; ++i) {
488         // check A(i,j,k) == B(resI, resJ) == array[k-1][j-1][i-1]
489         const CFI_index_t resSubcripts[]{resI, resJ};
490         const CFI_index_t srcSubcripts[]{i, j, k};
491         MATCH(true,
492             CFI_address(source, srcSubcripts) ==
493                 CFI_address(result, resSubcripts));
494         MATCH(true,
495             CFI_address(source, srcSubcripts) ==
496                 &array[k - lbs2][j - lbs1][i - lbs0]);
497         --resI;
498       }
499     }
500     ++resJ;
501   }
502 }
503 
504 static void run_CFI_select_part_tests() {
505   constexpr std::size_t name_len{5};
506   typedef struct {
507     double distance;
508     int stars;
509     char name[name_len];
510   } Galaxy;
511 
512   const CFI_rank_t rank{2};
513   constexpr CFI_index_t universeSize[]{2, 3};
514   Galaxy universe[universeSize[1]][universeSize[0]];
515 
516   for (int i{0}; i < universeSize[1]; ++i) {
517     for (int j{0}; j < universeSize[0]; ++j) {
518       // Initializing Fortran var universe(j,i)
519       universe[i][j].distance = j + i * 32;
520       universe[i][j].stars = j * 2 + i * 64;
521       universe[i][j].name[2] = static_cast<char>(j);
522       universe[i][j].name[3] = static_cast<char>(i);
523     }
524   }
525 
526   CFI_CDESC_T(rank) resStorage, srcStorage;
527   CFI_cdesc_t *result{&resStorage};
528   CFI_cdesc_t *source{&srcStorage};
529 
530   bool testPreConditions{true};
531   int retCode{CFI_establish(result, nullptr, CFI_attribute_other, CFI_type_int,
532       sizeof(int), rank, nullptr)};
533   testPreConditions &= (retCode == CFI_SUCCESS);
534   retCode = CFI_establish(source, &universe, CFI_attribute_other,
535       CFI_type_struct, sizeof(Galaxy), rank, universeSize);
536   testPreConditions &= (retCode == CFI_SUCCESS);
537   if (!testPreConditions) {
538     MATCH(true, testPreConditions);
539     return;
540   }
541 
542   std::size_t displacement{offsetof(Galaxy, stars)};
543   std::size_t elem_len{0}; // ignored
544   retCode = CFI_select_part(result, source, displacement, elem_len);
545   MATCH(CFI_SUCCESS, retCode);
546 
547   bool baseAddrShiftedOk{
548       static_cast<char *>(source->base_addr) + displacement ==
549       result->base_addr};
550   MATCH(true, baseAddrShiftedOk);
551   if (!baseAddrShiftedOk) {
552     return;
553   }
554 
555   MATCH(sizeof(int), result->elem_len);
556   for (CFI_index_t j{0}; j < universeSize[1]; ++j) {
557     for (CFI_index_t i{0}; i < universeSize[0]; ++i) {
558       CFI_index_t subscripts[]{
559           result->dim[0].lower_bound + i, result->dim[1].lower_bound + j};
560       MATCH(
561           i * 2 + j * 64, *static_cast<int *>(CFI_address(result, subscripts)));
562     }
563   }
564 
565   // Test for Fortran character type
566   retCode = CFI_establish(
567       result, nullptr, CFI_attribute_other, CFI_type_char, 2, rank, nullptr);
568   testPreConditions &= (retCode == CFI_SUCCESS);
569   if (!testPreConditions) {
570     MATCH(true, testPreConditions);
571     return;
572   }
573 
574   displacement = offsetof(Galaxy, name) + 2;
575   elem_len = 2; // not ignored this time
576   retCode = CFI_select_part(result, source, displacement, elem_len);
577   MATCH(CFI_SUCCESS, retCode);
578 
579   baseAddrShiftedOk = static_cast<char *>(source->base_addr) + displacement ==
580       result->base_addr;
581   MATCH(true, baseAddrShiftedOk);
582   if (!baseAddrShiftedOk) {
583     return;
584   }
585 
586   MATCH(elem_len, result->elem_len);
587   for (CFI_index_t j{0}; j < universeSize[1]; ++j) {
588     for (CFI_index_t i{0}; i < universeSize[0]; ++i) {
589       CFI_index_t subscripts[]{
590           result->dim[0].lower_bound + i, result->dim[1].lower_bound + j};
591       MATCH(static_cast<char>(i),
592           static_cast<char *>(CFI_address(result, subscripts))[0]);
593       MATCH(static_cast<char>(j),
594           static_cast<char *>(CFI_address(result, subscripts))[1]);
595     }
596   }
597 }
598 
599 static void run_CFI_setpointer_tests() {
600   constexpr CFI_rank_t rank{3};
601   CFI_CDESC_T(rank) resStorage, srcStorage;
602   CFI_cdesc_t *result{&resStorage};
603   CFI_cdesc_t *source{&srcStorage};
604   CFI_index_t lower_bounds[rank];
605   CFI_index_t extents[rank];
606   for (int i{0}; i < rank; ++i) {
607     lower_bounds[i] = i;
608     extents[i] = 2;
609   }
610 
611   char target;
612   char *dummyBaseAddress{&target};
613   bool testPreConditions{true};
614   CFI_type_t type{CFI_type_int};
615   std::size_t elem_len{ByteSize(type, 42)};
616   int retCode{CFI_establish(
617       result, nullptr, CFI_attribute_pointer, type, elem_len, rank, nullptr)};
618   testPreConditions &= (retCode == CFI_SUCCESS);
619   retCode = CFI_establish(source, dummyBaseAddress, CFI_attribute_other, type,
620       elem_len, rank, extents);
621   testPreConditions &= (retCode == CFI_SUCCESS);
622   if (!testPreConditions) {
623     MATCH(true, testPreConditions);
624     return;
625   }
626 
627   retCode = CFI_setpointer(result, source, lower_bounds);
628   MATCH(CFI_SUCCESS, retCode);
629 
630   // The following members must be invariant
631   MATCH(rank, result->rank);
632   MATCH(elem_len, result->elem_len);
633   MATCH(type, result->type);
634   // check pointer association
635   MATCH(true, result->base_addr == source->base_addr);
636   for (int j{0}; j < rank; ++j) {
637     MATCH(source->dim[j].extent, result->dim[j].extent);
638     MATCH(source->dim[j].sm, result->dim[j].sm);
639     MATCH(lower_bounds[j], result->dim[j].lower_bound);
640   }
641 }
642 
643 int main() {
644   TestCdescMacroForAllRanksSmallerThan<CFI_MAX_RANK>();
645   run_CFI_establish_tests();
646   run_CFI_address_tests();
647   run_CFI_allocate_tests();
648   // TODO: test CFI_deallocate
649   // TODO: test CFI_is_contiguous
650   run_CFI_section_tests();
651   run_CFI_select_part_tests();
652   run_CFI_setpointer_tests();
653   return testing::Complete();
654 }
655