This patch adds parsing and translation of the 'to' and 'from' clauses for the 'target update' construct in Fortran.
From cfb6b76da5bba038d854d510a4fd44ddf4fa8f1f Mon Sep 17 00:00:00 2001
From: Kwok Cheung Yeung <kcye...@baylibre.com>
Date: Mon, 2 Sep 2024 19:34:29 +0100
Subject: [PATCH 5/5] openmp, fortran: Add support for iterators in OpenMP
 'target update' constructs (Fortran)

This adds Fortran support for iterators in 'to' and 'from' clauses in the
'target update' OpenMP directive.

2024-09-02  Kwok Cheung Yeung  <kcye...@baylibre.com>

        gcc/fortran/
        * dump-parse-tree.cc (show_omp_namelist): Add iterator support for
        OMP_LIST_TO and OMP_LIST_FROM.
        * openmp.cc (gfc_free_omp_clauses): Free namespace for OMP_LIST_TO
        and OMP_LIST_FROM.
        (gfc_match_motion_var_list): Parse 'iterator' modifier.
        (resolve_omp_clauses): Resolve iterators for OMP_LIST_TO and
        OMP_LIST_FROM.
        * trans-openmp.cc (gfc_trans_omp_clauses): Handle iterators in
        OMP_LIST_TO and OMP_LIST_FROM clauses.

        gcc/testsuite/
        * gfortran.dg/gomp/target-update-iterator-1.f90: New.
        * gfortran.dg/gomp/target-update-iterator-2.f90: New.
        * gfortran.dg/gomp/target-update-iterator-3.f90: New.

        libgomp/
        * testsuite/libgomp.fortran/target-update-iterators-1.f90: New.
        * testsuite/libgomp.fortran/target-update-iterators-2.f90: New.
        * testsuite/libgomp.fortran/target-update-iterators-3.f90: New.
---
 gcc/fortran/dump-parse-tree.cc                |  7 +-
 gcc/fortran/openmp.cc                         | 62 +++++++++++++--
 gcc/fortran/trans-openmp.cc                   | 52 +++++++++++--
 .../gomp/target-update-iterator-1.f90         | 25 ++++++
 .../gomp/target-update-iterator-2.f90         | 22 ++++++
 .../gomp/target-update-iterator-3.f90         | 23 ++++++
 .../target-update-iterators-1.f90             | 68 ++++++++++++++++
 .../target-update-iterators-2.f90             | 62 +++++++++++++++
 .../target-update-iterators-3.f90             | 77 +++++++++++++++++++
 9 files changed, 386 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/target-update-iterator-1.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/target-update-iterator-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/target-update-iterator-3.f90
 create mode 100644 
libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90
 create mode 100644 
libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90
 create mode 100644 
libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90

diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc
index 0272a443f65..1a602fb953c 100644
--- a/gcc/fortran/dump-parse-tree.cc
+++ b/gcc/fortran/dump-parse-tree.cc
@@ -1350,7 +1350,8 @@ show_omp_namelist (int list_type, gfc_omp_namelist *n)
     {
       gfc_current_ns = ns_curr;
       if (list_type == OMP_LIST_AFFINITY || list_type == OMP_LIST_DEPEND
-         || list_type == OMP_LIST_MAP)
+         || list_type == OMP_LIST_MAP
+         || list_type == OMP_LIST_TO || list_type == OMP_LIST_FROM)
        {
          gfc_current_ns = n->u2.ns ? n->u2.ns : ns_curr;
          if (n->u2.ns != ns_iter)
@@ -1366,6 +1367,10 @@ show_omp_namelist (int list_type, gfc_omp_namelist *n)
                    fputs ("DEPEND (", dumpfile);
                  else if (list_type == OMP_LIST_MAP)
                    fputs ("MAP (", dumpfile);
+                 else if (list_type == OMP_LIST_TO)
+                   fputs ("TO (", dumpfile);
+                 else if (list_type == OMP_LIST_FROM)
+                   fputs ("FROM (", dumpfile);
                  else
                    gcc_unreachable ();
                }
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index 996126e6e7f..4eb4a8e53e2 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -192,7 +192,8 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
   for (i = 0; i < OMP_LIST_NUM; i++)
     gfc_free_omp_namelist (c->lists[i],
                           i == OMP_LIST_AFFINITY || i == OMP_LIST_DEPEND
-                          || i == OMP_LIST_MAP,
+                          || i == OMP_LIST_MAP
+                          || i == OMP_LIST_TO || i == OMP_LIST_FROM,
                           i == OMP_LIST_ALLOCATE,
                           i == OMP_LIST_USES_ALLOCATORS);
   gfc_free_expr_list (c->wait_list);
@@ -1362,17 +1363,65 @@ gfc_match_motion_var_list (const char *str, 
gfc_omp_namelist **list,
   if (m != MATCH_YES)
     return m;
 
-  match m_present = gfc_match (" present : ");
+  gfc_namespace *ns_iter = NULL, *ns_curr = gfc_current_ns;
+  int present_modifier = 0, iterator_modifier = 0;
+  locus present_locus = gfc_current_locus, iterator_locus = gfc_current_locus;
 
-  m = gfc_match_omp_variable_list ("", list, false, NULL, headp, true, true);
+  for (;;)
+    {
+      locus current_locus = gfc_current_locus;
+      if (gfc_match ("present ") == MATCH_YES)
+       {
+         if (present_modifier++ == 1)
+           present_locus = current_locus;
+       }
+      else if (gfc_match_iterator (&ns_iter, true) == MATCH_YES)
+       {
+         if (iterator_modifier++ == 1)
+           iterator_locus = current_locus;
+       }
+      else
+       break;
+      gfc_match (", ");
+    }
+
+  if (present_modifier > 1)
+    {
+      gfc_error ("too many %<present%> modifiers at %L",
+                &present_locus);
+      return MATCH_ERROR;
+    }
+  if (iterator_modifier > 1)
+    {
+      gfc_error ("too many %<iterator%> modifiers at %L",
+                &iterator_locus);
+      return MATCH_ERROR;
+    }
+
+  if (ns_iter)
+    gfc_current_ns = ns_iter;
+
+  const char *exp = (present_modifier || iterator_modifier) ? " :" : "";
+  m = gfc_match_omp_variable_list (exp, list, false, NULL, headp, true, true);
+  gfc_current_ns = ns_curr;
   if (m != MATCH_YES)
     return m;
-  if (m_present == MATCH_YES)
+
+  if (present_modifier || iterator_modifier)
     {
       gfc_omp_namelist *n;
       for (n = **headp; n; n = n->next)
-       n->u.present_modifier = true;
+       {
+         if (present_modifier)
+           n->u.present_modifier = true;
+         if (iterator_modifier)
+           {
+             n->u2.ns = ns_iter;
+             ns_iter->refs++;
+           }
+       }
     }
+
   return MATCH_YES;
 }
 
@@ -8436,7 +8485,8 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses 
*omp_clauses,
            for (; n != NULL; n = n->next)
              {
                if ((list == OMP_LIST_DEPEND || list == OMP_LIST_AFFINITY
-                    || list == OMP_LIST_MAP)
+                    || list == OMP_LIST_MAP
+                    || list == OMP_LIST_TO || list == OMP_LIST_FROM)
                    && n->u2.ns && !n->u2.ns->resolved)
                  {
                    n->u2.ns->resolved = 1;
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index a9929430e53..1be8f2ad806 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -4052,11 +4052,40 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
        case OMP_LIST_TO:
        case OMP_LIST_FROM:
        case OMP_LIST_CACHE:
+         iterator = NULL_TREE;
+         prev = NULL;
+         prev_clauses = omp_clauses;
          for (; n != NULL; n = n->next)
            {
              if (!n->sym->attr.referenced)
                continue;
 
+             if (iterator && prev->u2.ns != n->u2.ns)
+               {
+                 /* Finish previous iterator group.  */
+                 BLOCK_SUBBLOCKS (tree_block) = gfc_finish_block (&iter_block);
+                 TREE_VEC_ELT (iterator, 5) = tree_block;
+                 for (tree c = omp_clauses; c != prev_clauses;
+                      c = OMP_CLAUSE_CHAIN (c))
+                   OMP_CLAUSE_DECL (c) = build_tree_list (iterator,
+                                                          OMP_CLAUSE_DECL (c));
+                 prev_clauses = omp_clauses;
+                 iterator = NULL_TREE;
+               }
+             if (n->u2.ns && (!prev || prev->u2.ns != n->u2.ns))
+               {
+                 /* Start a new iterator group.  */
+                 gfc_init_block (&iter_block);
+                 tree_block = make_node (BLOCK);
+                 TREE_USED (tree_block) = 1;
+                 BLOCK_VARS (tree_block) = NULL_TREE;
+                 prev_clauses = omp_clauses;
+                 iterator = handle_iterator (n->u2.ns, block, tree_block);
+               }
+             if (!iterator)
+               gfc_init_block (&iter_block);
+             prev = n;
+
              switch (list)
                {
                case OMP_LIST_TO:
@@ -4094,7 +4123,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                      ptr = build_fold_indirect_ref (ptr);
                      OMP_CLAUSE_DECL (node) = ptr;
                      OMP_CLAUSE_SIZE (node)
-                       = gfc_full_array_size (block, decl,
+                       = gfc_full_array_size (&iter_block, decl,
                                               GFC_TYPE_ARRAY_RANK (type));
                      tree elemsz
                        = TYPE_SIZE_UNIT (gfc_get_element_type (type));
@@ -4119,7 +4148,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                    {
                      gfc_conv_expr_reference (&se, n->expr);
                      ptr = se.expr;
-                     gfc_add_block_to_block (block, &se.pre);
+                     gfc_add_block_to_block (&iter_block, &se.pre);
                      OMP_CLAUSE_SIZE (node)
                        = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ptr)));
                    }
@@ -4128,9 +4157,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                      gfc_conv_expr_descriptor (&se, n->expr);
                      ptr = gfc_conv_array_data (se.expr);
                      tree type = TREE_TYPE (se.expr);
-                     gfc_add_block_to_block (block, &se.pre);
+                     gfc_add_block_to_block (&iter_block, &se.pre);
                      OMP_CLAUSE_SIZE (node)
-                       = gfc_full_array_size (block, se.expr,
+                       = gfc_full_array_size (&iter_block, se.expr,
                                               GFC_TYPE_ARRAY_RANK (type));
                      tree elemsz
                        = TYPE_SIZE_UNIT (gfc_get_element_type (type));
@@ -4139,7 +4168,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                        = fold_build2 (MULT_EXPR, gfc_array_index_type,
                                       OMP_CLAUSE_SIZE (node), elemsz);
                    }
-                 gfc_add_block_to_block (block, &se.post);
+                 gfc_add_block_to_block (&iter_block, &se.post);
                  gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
                  OMP_CLAUSE_DECL (node) = build_fold_indirect_ref (ptr);
                }
@@ -4147,8 +4176,21 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                OMP_CLAUSE_MOTION_PRESENT (node) = 1;
              if (list == OMP_LIST_CACHE && n->u.map.readonly)
                OMP_CLAUSE__CACHE__READONLY (node) = 1;
+
+             if (!iterator)
+               gfc_add_block_to_block (block, &iter_block);
              omp_clauses = gfc_trans_add_clause (node, omp_clauses);
            }
+         if (iterator)
+           {
+             /* Finish last iterator group.  */
+             BLOCK_SUBBLOCKS (tree_block) = gfc_finish_block (&iter_block);
+             TREE_VEC_ELT (iterator, 5) = tree_block;
+             for (tree c = omp_clauses; c != prev_clauses;
+               c = OMP_CLAUSE_CHAIN (c))
+             OMP_CLAUSE_DECL (c) = build_tree_list (iterator,
+                                                    OMP_CLAUSE_DECL (c));
+           }
          break;
        case OMP_LIST_USES_ALLOCATORS:
          /* Ignore pre-defined allocators as no special treatment is needed. */
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-1.f90 
b/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-1.f90
new file mode 100644
index 00000000000..08dc3d79911
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-1.f90
@@ -0,0 +1,25 @@
+! { dg-do compile }
+! { dg-options "-fopenmp" }
+
+program test
+  implicit none
+
+  integer, parameter :: DIM1 = 17
+  integer, parameter :: DIM2 = 39
+
+  type :: array_ptr
+    integer, pointer :: ptr(:)
+  end type
+
+  type (array_ptr) :: x(DIM1), y(DIM1)
+
+  !$omp target update to (iterator(i=1:DIM1): x(i)%ptr(:))
+
+  !$omp target update to (iterator(i=1:DIM1): x(i)%ptr(:DIM2), y(i)%ptr(:))
+
+  !$omp target update to (iterator(i=1:DIM1), present: x(i)%ptr(:))
+
+  !$omp target update to (iterator(i=1:DIM1), iterator(j=i:DIM2): x(i)%ptr(j)) 
! { dg-error "too many 'iterator' modifiers at .1." }
+
+  !$omp target update to (iterator(i=1:DIM1), something: x(i, j)) ! { dg-error 
"Failed to match clause at .1." }
+end program
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-2.f90 
b/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-2.f90
new file mode 100644
index 00000000000..89f645bda23
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-2.f90
@@ -0,0 +1,22 @@
+! { dg-do compile }
+! { dg-options "-fopenmp" }
+
+program test
+  implicit none
+
+  integer, parameter :: DIM1 = 17
+  integer, parameter :: DIM2 = 39
+
+  type :: array_ptr
+    integer, pointer :: ptr(:)
+  end type
+
+  type (array_ptr) :: x(DIM1), y(DIM1), z(DIM1)
+
+  !$omp target update to(iterator(i=1:10): x) ! { dg-error "iterator variable 
.i. not used in clause expression" }
+  !$omp target update from(iterator(i=1:10, j=1:20): x(i)) ! { dg-error 
"iterator variable .j. not used in clause expression" }
+  !$omp target update to(iterator(i=1:10, j=1:20, k=1:30): x(i), y(j), z(k))
+  ! { dg-error "iterator variable .i. not used in clause expression" "" { 
target *-*-* } .-1 }
+  ! { dg-error "iterator variable .j. not used in clause expression" "" { 
target *-*-* } .-2 }
+  ! { dg-error "iterator variable .k. not used in clause expression" "" { 
target *-*-* } .-3 }
+end program
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-3.f90 
b/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-3.f90
new file mode 100644
index 00000000000..a8b8ce8f171
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-update-iterator-3.f90
@@ -0,0 +1,23 @@
+! { dg-do compile }
+! { dg-options "-fopenmp -fdump-tree-gimple" }
+
+program test
+  implicit none
+
+  integer, parameter :: DIM1 = 17
+  integer, parameter :: DIM2 = 39
+
+  type :: array_ptr
+    integer, pointer :: ptr(:)
+  end type
+
+  type (array_ptr) :: x(DIM1, DIM2), y(DIM1, DIM2), z(DIM1)
+
+  !$omp target update to (iterator(i=1:DIM1, j=1:DIM2): x(i, j)%ptr(:), y(i, 
j)%ptr(:))
+  !$omp target update from (iterator(i=1:DIM1): z(i)%ptr(:))
+end program
+
+! { dg-final { scan-tree-dump-times "if \\(i <= 17\\) goto <D\.\[0-9\]+>; else 
goto <D\.\[0-9\]+>;" 2 "gimple" } }
+! { dg-final { scan-tree-dump-times "if \\(j <= 39\\) goto <D\.\[0-9\]+>; else 
goto <D\.\[0-9\]+>;" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "to\\(iterator\\(integer\\(kind=4\\) 
j=1:39:1, integer\\(kind=4\\) i=1:17:1\\):iterator_array=D\.\[0-9\]+" 2 
"gimple" } }
+! { dg-final { scan-tree-dump-times "from\\(iterator\\(integer\\(kind=4\\) 
i=1:17:1\\):iterator_array=D\.\[0-9\]+" 1 "gimple" } }
diff --git a/libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90 
b/libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90
new file mode 100644
index 00000000000..e9a13a3c737
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90
@@ -0,0 +1,68 @@
+! { dg-do run }
+
+! Test target enter data and target update to the target using map
+! iterators.
+
+program test
+  integer, parameter :: DIM1 = 8
+  integer, parameter :: DIM2 = 15
+
+  type :: array_ptr
+    integer, pointer :: arr(:)
+  end type
+
+  type (array_ptr) :: x(DIM1)
+  integer :: expected, sum, i, j
+
+  expected = mkarray (x)
+
+  !$omp target enter data map(to: x)
+  !$omp target enter data map(iterator(i=1:DIM1), to: x(i)%arr(:))
+  !$omp target map(from: sum)
+    sum = 0
+    do i = 1, DIM1
+      do j = 1, DIM2
+       sum = sum + x(i)%arr(j)
+      end do
+    end do
+  !$omp end target
+
+  print *, sum, expected
+  if (sum .ne. expected) stop 1
+
+  expected = 0
+  do i = 1, DIM1
+    do j = 1, DIM2
+      x(i)%arr(j) = x(i)%arr(j) * i * j
+      expected = expected + x(i)%arr(j)
+    end do
+  end do
+
+  !$omp target update to(iterator(i=1:DIM1): x(i)%arr(:))
+
+  !$omp target map(from: sum)
+    sum = 0
+    do i = 1, DIM1
+      do j = 1, DIM2
+       sum = sum + x(i)%arr(j)
+      end do
+    end do
+  !$omp end target
+
+  if (sum .ne. expected) stop 2
+contains
+  integer function mkarray (x)
+    type (array_ptr), intent(inout) :: x(DIM1)
+    integer :: exp = 0
+
+    do i = 1, DIM1
+      allocate (x(i)%arr(DIM2))
+      do j = 1, DIM2
+       x(i)%arr(j) = i * j
+       exp = exp + x(i)%arr(j)
+      end do
+    end do
+
+    mkarray = exp
+  end function
+end program
diff --git a/libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90 
b/libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90
new file mode 100644
index 00000000000..6c1a8a7b5dd
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90
@@ -0,0 +1,62 @@
+! { dg-do run }
+
+! Test target enter data and target update from the target using map
+! iterators.
+
+program test
+  integer, parameter :: DIM1 = 8
+  integer, parameter :: DIM2 = 15
+
+  type :: array_ptr
+    integer, pointer :: arr(:)
+  end type
+
+  type (array_ptr) :: x(DIM1)
+  integer :: sum, expected
+
+  call mkarray (x)
+
+  !$omp target enter data map(to: x(:DIM1))
+  !$omp target enter data map(iterator(i=1:DIM1), to: x(i)%arr(:))
+  !$omp target map(from: expected)
+    expected = 0
+    do i = 1, DIM1
+      do j = 1, DIM2
+       x(i)%arr(j) = (i + 1) * (j + 2)
+       expected = expected + x(i)%arr(j)
+      end do
+    end do
+  !$omp end target
+
+  ! Host copy of x should remain unchanged.
+  sum = 0
+  do i = 1, DIM1
+    do j = 1, DIM2
+      sum = sum + x(i)%arr(j)
+    end do
+  end do
+  if (sum .ne. 0) stop 1
+
+  !$omp target update from(iterator(i=1:DIM1): x(i)%arr(:))
+
+  ! Host copy should now be updated.
+  sum = 0
+  do i = 1, DIM1
+    do j = 1, DIM2
+      sum = sum + x(i)%arr(j)
+    end do
+  end do
+
+  if (sum .ne. expected) stop 2
+contains
+  subroutine mkarray (x)
+    type (array_ptr), intent(inout) :: x(DIM1)
+
+    do i = 1, DIM1
+      allocate (x(i)%arr(DIM2))
+      do j = 1, DIM2
+       x(i)%arr(j) = 0
+      end do
+    end do
+  end subroutine
+end program
diff --git a/libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90 
b/libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90
new file mode 100644
index 00000000000..8dbfb45fe8d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90
@@ -0,0 +1,77 @@
+! { dg-do run }
+
+! Test target enter data and target update to the target using map
+! iterators with a function.
+
+program test
+  implicit none
+
+  integer, parameter :: DIM1 = 8
+  integer, parameter :: DIM2 = 15
+
+  type :: array_ptr
+    integer, pointer :: arr(:)
+  end type
+
+  type (array_ptr) :: x(DIM1)
+  integer :: x_new(DIM1, DIM2)
+  integer :: expected, sum, i, j
+
+  call mkarray (x)
+
+  !$omp target enter data map(to: x(:DIM1))
+  !$omp target enter data map(iterator(i=1:DIM1), to: x(i)%arr(:))
+
+  ! Update x on host.
+  do i = 1, DIM1
+    do j = 1, DIM2
+      x_new(i, j) = x(i)%arr(j)
+      x(i)%arr(j) = (i + 1) * (j + 2);
+    end do
+  end do
+
+  ! Update a subset of x on target.
+  !$omp target update to(iterator(i=1:DIM1/2): x(f (i))%arr(:))
+
+  !$omp target map(from: sum)
+    sum = 0
+    do i = 1, DIM1
+      do j = 1, DIM2
+       sum = sum + x(i)%arr(j)
+      end do
+    end do
+  !$omp end target
+
+  ! Calculate expected value on host.
+  do i = 1, DIM1/2
+    do j = 1, DIM2
+      x_new(f (i), j) = x(f (i))%arr(j)
+    end do
+  end do
+
+  expected = 0
+  do i = 1, DIM1
+    do j = 1, DIM2
+      expected = expected + x_new(i, j)
+    end do
+  end do
+
+  if (sum .ne. expected) stop 1
+contains
+  subroutine mkarray (x)
+    type (array_ptr), intent(inout) :: x(DIM1)
+
+    do i = 1, DIM1
+      allocate (x(i)%arr(DIM2))
+      do j = 1, DIM2
+       x(i)%arr(j) = i * j
+      end do
+    end do
+  end subroutine
+
+  integer function f (i)
+    integer, intent(in) :: i
+
+    f = i * 2
+  end function
+end program
-- 
2.34.1

Reply via email to