Hi Harald, Sorry about that - it was the standard HEAD versus HEAD~ mistake.
Thanks for pointing it out. Paul On Fri, 29 Nov 2024 at 17:31, Harald Anlauf <anl...@gmx.de> wrote: > Hi Paul, > > the patch seems to contain stuff that has already been pushed > (gcc/testsuite/gfortran.dg/pr117768.f90, and the chunks in > class.cc and resolve.cc). Can you please check? > > Cheers, > Harald > > Am 29.11.24 um 17:34 schrieb Paul Richard Thomas: > > Hi All, > > > > This patch was originally pushed as r15-2739. Subsequently memory faults > > were found and so the patch was reverted. At the time, I could find where > > the problem lay. This morning I had another look and found it almost > > immediately :-) > > > > The fix is the 'gfc_resize_class_size_with_len' in the chunk '@@ -1595,14 > > +1629,51 @@ gfc_trans_create_temp_array '. Without it,, half as much > memory > > as needed was being provided by the allocation and so accesses were > > occurring outside the allocated space. Valgrind now reports no errors. > > > > Regression tests with flying colours - OK for mainline? > > > > Paul > > > >
diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc index a458af322ce..870f2920ddc 100644 --- a/gcc/fortran/trans-array.cc +++ b/gcc/fortran/trans-array.cc @@ -1325,23 +1325,28 @@ get_array_ref_dim_for_loop_dim (gfc_ss *ss, int loop_dim) is a class expression. */ static tree -get_class_info_from_ss (stmtblock_t * pre, gfc_ss *ss, tree *eltype) +get_class_info_from_ss (stmtblock_t * pre, gfc_ss *ss, tree *eltype, + gfc_ss **fcnss) { + gfc_ss *loop_ss = ss->loop->ss; gfc_ss *lhs_ss; gfc_ss *rhs_ss; + gfc_ss *fcn_ss = NULL; tree tmp; tree tmp2; tree vptr; - tree rhs_class_expr = NULL_TREE; + tree class_expr = NULL_TREE; tree lhs_class_expr = NULL_TREE; bool unlimited_rhs = false; bool unlimited_lhs = false; bool rhs_function = false; + bool unlimited_arg1 = false; gfc_symbol *vtab; + tree cntnr = NULL_TREE; /* The second element in the loop chain contains the source for the - temporary; ie. the rhs of the assignment. */ - rhs_ss = ss->loop->ss->loop_chain; + class temporary created in gfc_trans_create_temp_array. */ + rhs_ss = loop_ss->loop_chain; if (rhs_ss != gfc_ss_terminator && rhs_ss->info @@ -1350,28 +1355,58 @@ get_class_info_from_ss (stmtblock_t * pre, gfc_ss *ss, tree *eltype) && rhs_ss->info->data.array.descriptor) { if (rhs_ss->info->expr->expr_type != EXPR_VARIABLE) - rhs_class_expr + class_expr = gfc_get_class_from_expr (rhs_ss->info->data.array.descriptor); else - rhs_class_expr = gfc_get_class_from_gfc_expr (rhs_ss->info->expr); + class_expr = gfc_get_class_from_gfc_expr (rhs_ss->info->expr); unlimited_rhs = UNLIMITED_POLY (rhs_ss->info->expr); if (rhs_ss->info->expr->expr_type == EXPR_FUNCTION) rhs_function = true; } + /* Usually, ss points to the function. When the function call is an actual + argument, it is instead rhs_ss because the ss chain is shifted by one. */ + *fcnss = fcn_ss = rhs_function ? rhs_ss : ss; + + /* If this is a transformational function with a class result, the info + class_container field points to the class container of arg1. */ + if (class_expr != NULL_TREE + && fcn_ss->info && fcn_ss->info->expr + && fcn_ss->info->expr->expr_type == EXPR_FUNCTION + && fcn_ss->info->expr->value.function.isym + && fcn_ss->info->expr->value.function.isym->transformational) + { + cntnr = ss->info->class_container; + unlimited_arg1 + = UNLIMITED_POLY (fcn_ss->info->expr->value.function.actual->expr); + } + /* For an assignment the lhs is the next element in the loop chain. If we have a class rhs, this had better be a class variable - expression! */ + expression! Otherwise, the class container from arg1 can be used + to set the vptr and len fields of the result class container. */ lhs_ss = rhs_ss->loop_chain; - if (lhs_ss != gfc_ss_terminator - && lhs_ss->info - && lhs_ss->info->expr + if (lhs_ss && lhs_ss != gfc_ss_terminator + && lhs_ss->info && lhs_ss->info->expr && lhs_ss->info->expr->expr_type ==EXPR_VARIABLE && lhs_ss->info->expr->ts.type == BT_CLASS) { tmp = lhs_ss->info->data.array.descriptor; unlimited_lhs = UNLIMITED_POLY (rhs_ss->info->expr); } + else if (cntnr != NULL_TREE) + { + tmp = gfc_class_vptr_get (class_expr); + gfc_add_modify (pre, tmp, fold_convert (TREE_TYPE (tmp), + gfc_class_vptr_get (cntnr))); + if (unlimited_rhs) + { + tmp = gfc_class_len_get (class_expr); + if (unlimited_arg1) + gfc_add_modify (pre, tmp, gfc_class_len_get (cntnr)); + } + tmp = NULL_TREE; + } else tmp = NULL_TREE; @@ -1379,35 +1414,33 @@ get_class_info_from_ss (stmtblock_t * pre, gfc_ss *ss, tree *eltype) if (tmp != NULL_TREE && lhs_ss->loop_chain == gfc_ss_terminator) lhs_class_expr = gfc_get_class_from_expr (tmp); else - return rhs_class_expr; + return class_expr; gcc_assert (GFC_CLASS_TYPE_P (TREE_TYPE (lhs_class_expr))); /* Set the lhs vptr and, if necessary, the _len field. */ - if (rhs_class_expr) + if (class_expr) { /* Both lhs and rhs are class expressions. */ tmp = gfc_class_vptr_get (lhs_class_expr); gfc_add_modify (pre, tmp, fold_convert (TREE_TYPE (tmp), - gfc_class_vptr_get (rhs_class_expr))); + gfc_class_vptr_get (class_expr))); if (unlimited_lhs) { + gcc_assert (unlimited_rhs); tmp = gfc_class_len_get (lhs_class_expr); - if (unlimited_rhs) - tmp2 = gfc_class_len_get (rhs_class_expr); - else - tmp2 = build_int_cst (TREE_TYPE (tmp), 0); + tmp2 = gfc_class_len_get (class_expr); gfc_add_modify (pre, tmp, tmp2); } if (rhs_function) { - tmp = gfc_class_data_get (rhs_class_expr); + tmp = gfc_class_data_get (class_expr); gfc_conv_descriptor_offset_set (pre, tmp, gfc_index_zero_node); } } - else + else if (rhs_ss->info->data.array.descriptor) { /* lhs is class and rhs is intrinsic or derived type. */ *eltype = TREE_TYPE (rhs_ss->info->data.array.descriptor); @@ -1435,7 +1468,7 @@ get_class_info_from_ss (stmtblock_t * pre, gfc_ss *ss, tree *eltype) } } - return rhs_class_expr; + return class_expr; } @@ -1476,6 +1509,7 @@ gfc_trans_create_temp_array (stmtblock_t * pre, stmtblock_t * post, gfc_ss * ss, tree or_expr; tree elemsize; tree class_expr = NULL_TREE; + gfc_ss *fcn_ss = NULL; int n, dim, tmp_dim; int total_dim = 0; @@ -1495,7 +1529,7 @@ gfc_trans_create_temp_array (stmtblock_t * pre, stmtblock_t * post, gfc_ss * ss, The descriptor can be obtained from the ss->info and then converted to the class object. */ if (class_expr == NULL_TREE && GFC_CLASS_TYPE_P (eltype)) - class_expr = get_class_info_from_ss (pre, ss, &eltype); + class_expr = get_class_info_from_ss (pre, ss, &eltype, &fcn_ss); /* If the dynamic type is not available, use the declared type. */ if (eltype && GFC_CLASS_TYPE_P (eltype)) @@ -1595,14 +1629,51 @@ gfc_trans_create_temp_array (stmtblock_t * pre, stmtblock_t * post, gfc_ss * ss, gfc_add_expr_to_block (pre, build1 (DECL_EXPR, arraytype, TYPE_NAME (arraytype))); - if (class_expr != NULL_TREE) + if (class_expr != NULL_TREE + || (fcn_ss && fcn_ss->info && fcn_ss->info->class_container)) { tree class_data; tree dtype; + gfc_expr *expr1 = fcn_ss ? fcn_ss->info->expr : NULL; - /* Create a class temporary. */ - tmp = gfc_create_var (TREE_TYPE (class_expr), "ctmp"); - gfc_add_modify (pre, tmp, class_expr); + /* Create a class temporary for the result using the lhs class object. */ + if (class_expr != NULL_TREE) + { + tmp = gfc_create_var (TREE_TYPE (class_expr), "ctmp"); + gfc_add_modify (pre, tmp, class_expr); + } + else + { + tree vptr; + class_expr = fcn_ss->info->class_container; + gcc_assert (expr1); + + /* Build a new class container using the arg1 class object. The class + typespec must be rebuilt because the rank might have changed. */ + gfc_typespec ts = CLASS_DATA (expr1)->ts; + symbol_attribute attr = CLASS_DATA (expr1)->attr; + gfc_change_class (&ts, &attr, NULL, expr1->rank, 0); + tmp = gfc_create_var (gfc_typenode_for_spec (&ts), "ctmp"); + fcn_ss->info->class_container = tmp; + + /* Set the vptr and obtain the element size. */ + vptr = gfc_class_vptr_get (tmp); + gfc_add_modify (pre, vptr, + fold_convert (TREE_TYPE (vptr), + gfc_class_vptr_get (class_expr))); + elemsize = gfc_class_vtab_size_get (class_expr); + + /* Set the _len field, if necessary. */ + if (UNLIMITED_POLY (expr1)) + { + gfc_add_modify (pre, gfc_class_len_get (tmp), + gfc_class_len_get (class_expr)); + elemsize = gfc_resize_class_size_with_len (pre, class_expr, + elemsize); + } + + elemsize = gfc_evaluate_now (elemsize, pre); + } /* Assign the new descriptor to the _data field. This allows the vptr _copy to be used for scalarized assignment since the class @@ -1612,11 +1683,25 @@ gfc_trans_create_temp_array (stmtblock_t * pre, stmtblock_t * post, gfc_ss * ss, TREE_TYPE (desc), desc); gfc_add_modify (pre, class_data, tmp); - /* Take the dtype from the class expression. */ - dtype = gfc_conv_descriptor_dtype (gfc_class_data_get (class_expr)); - tmp = gfc_conv_descriptor_dtype (class_data); - gfc_add_modify (pre, tmp, dtype); + if (expr1 && expr1->expr_type == EXPR_FUNCTION + && expr1->value.function.isym + && (expr1->value.function.isym->id == GFC_ISYM_RESHAPE + || expr1->value.function.isym->id == GFC_ISYM_UNPACK)) + { + /* Take the dtype from the class expression. */ + dtype = gfc_conv_descriptor_dtype (gfc_class_data_get (class_expr)); + tmp = gfc_conv_descriptor_dtype (class_data); + gfc_add_modify (pre, tmp, dtype); + /* Transformational functions reshape and reduce can change the rank. */ + if (fcn_ss && fcn_ss->info && fcn_ss->info->class_container) + { + tmp = gfc_conv_descriptor_rank (class_data); + gfc_add_modify (pre, tmp, + build_int_cst (TREE_TYPE (tmp), ss->loop->dimen)); + fcn_ss->info->class_container = NULL_TREE; + } + } /* Point desc to the class _data field. */ desc = class_data; } @@ -6073,6 +6158,14 @@ gfc_array_init_size (tree descriptor, int rank, int corank, tree * poffset, tmp = gfc_conv_descriptor_dtype (descriptor); gfc_add_modify (pblock, tmp, gfc_conv_descriptor_dtype (expr3_desc)); } + else if (expr->ts.type == BT_CLASS + && expr3 && expr3->ts.type != BT_CLASS + && expr3_elem_size != NULL_TREE && expr3_desc == NULL_TREE) + { + tmp = gfc_conv_descriptor_elem_len (descriptor); + gfc_add_modify (pblock, tmp, + fold_convert (TREE_TYPE (tmp), expr3_elem_size)); + } else { tmp = gfc_conv_descriptor_dtype (descriptor); diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index 41d06a99f75..3718b0e645b 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -1242,6 +1242,21 @@ gfc_conv_class_to_class (gfc_se *parmse, gfc_expr *e, gfc_typespec class_ts, stmtblock_t block; bool full_array = false; + /* Class transformational function results are the data field of a class + temporary and so the class expression can be obtained directly. */ + if (e->expr_type == EXPR_FUNCTION + && e->value.function.isym + && e->value.function.isym->transformational + && TREE_CODE (parmse->expr) == COMPONENT_REF + && !GFC_CLASS_TYPE_P (TREE_TYPE (parmse->expr))) + { + parmse->expr = TREE_OPERAND (parmse->expr, 0); + if (!VAR_P (parmse->expr)) + parmse->expr = gfc_evaluate_now (parmse->expr, &parmse->pre); + parmse->expr = gfc_build_addr_expr (NULL_TREE, parmse->expr); + return; + } + gfc_init_block (&block); class_ref = NULL; @@ -6490,7 +6505,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, gfc_component *comp = NULL; int arglen; unsigned int argc; - + tree arg1_cntnr = NULL_TREE; arglist = NULL; retargs = NULL; stringargs = NULL; @@ -6498,6 +6513,8 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, var = NULL_TREE; len = NULL_TREE; gfc_clear_ts (&ts); + gfc_intrinsic_sym *isym = expr && expr->rank ? + expr->value.function.isym : NULL; comp = gfc_get_proc_ptr_comp (expr); @@ -7601,6 +7618,19 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, e->representation.length); } + /* Make the class container for the first argument available with class + valued transformational functions. */ + if (argc == 0 && e && e->ts.type == BT_CLASS + && isym && isym->transformational + && se->ss && se->ss->info) + { + arg1_cntnr = parmse.expr; + if (POINTER_TYPE_P (TREE_TYPE (arg1_cntnr))) + arg1_cntnr = build_fold_indirect_ref_loc (input_location, arg1_cntnr); + arg1_cntnr = gfc_get_class_from_expr (arg1_cntnr); + se->ss->info->class_container = arg1_cntnr; + } + if (fsym && e) { /* Obtain the character length of an assumed character length @@ -8211,6 +8241,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, /* Set the type of the array. */ tmp = gfc_typenode_for_spec (&ts); + tmp = arg1_cntnr ? TREE_TYPE (arg1_cntnr) : tmp; gcc_assert (se->ss->dimen == se->loop->dimen); /* Evaluate the bounds of the result, if known. */ @@ -8495,8 +8526,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, argument is actually given. */ arg = expr->value.function.actual; if (result && arg && expr->rank - && expr->value.function.isym - && expr->value.function.isym->transformational + && isym && isym->transformational && arg->expr && arg->expr->ts.type == BT_DERIVED && arg->expr->ts.u.derived->attr.alloc_comp) @@ -11495,7 +11525,7 @@ realloc_lhs_loop_for_fcn_call (gfc_se *se, locus *where, gfc_ss **ss, result to the original descriptor. */ static void -fcncall_realloc_result (gfc_se *se, int rank) +fcncall_realloc_result (gfc_se *se, int rank, tree dtype) { tree desc; tree res_desc; @@ -11514,7 +11544,10 @@ fcncall_realloc_result (gfc_se *se, int rank) /* Unallocated, the descriptor does not have a dtype. */ tmp = gfc_conv_descriptor_dtype (desc); - gfc_add_modify (&se->pre, tmp, gfc_get_dtype (TREE_TYPE (desc))); + if (dtype != NULL_TREE) + gfc_add_modify (&se->pre, tmp, dtype); + else + gfc_add_modify (&se->pre, tmp, gfc_get_dtype (TREE_TYPE (desc))); res_desc = gfc_evaluate_now (desc, &se->pre); gfc_conv_descriptor_data_set (&se->pre, res_desc, null_pointer_node); @@ -11731,7 +11764,19 @@ gfc_trans_arrayfunc_assign (gfc_expr * expr1, gfc_expr * expr2) ss->is_alloc_lhs = 1; } else - fcncall_realloc_result (&se, expr1->rank); + { + tree dtype = NULL_TREE; + tree type = gfc_typenode_for_spec (&expr2->ts); + if (expr1->ts.type == BT_CLASS) + { + tmp = gfc_class_vptr_get (sym->backend_decl); + tree tmp2 = gfc_get_symbol_decl (gfc_find_vtab (&expr2->ts)); + tmp2 = gfc_build_addr_expr (TREE_TYPE (tmp), tmp2); + gfc_add_modify (&se.pre, tmp, tmp2); + dtype = gfc_get_dtype_rank_type (expr1->rank,type); + } + fcncall_realloc_result (&se, expr1->rank, dtype); + } } gfc_conv_function_expr (&se, expr2); diff --git a/gcc/testsuite/gfortran.dg/class_transformational_1.f90 b/gcc/testsuite/gfortran.dg/class_transformational_1.f90 new file mode 100644 index 00000000000..42e30926a05 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/class_transformational_1.f90 @@ -0,0 +1,206 @@ +! { dg-do run } +! +! Test transformational intrinsics with class results - PR102689 +! +! Contributed by Tobias Burnus <bur...@gcc.gnu.org> +! +module tests + type t + integer :: i + end type t + type, extends(t) :: s + integer :: j + end type + +contains + + subroutine class_bar(x) + class(*), intent(in) :: x(..) + integer :: checksum + + if (product (shape (x)) .ne. 10) stop 1 + select rank (x) + rank (1) + select type (x) + type is (s) + if (sum(x%i) .ne. 55) stop 2 + if ((sum(x%j) .ne. 550) .and. (sum(x%j) .ne. 110)) stop 3 + type is (character(*)) + checksum = sum(ichar(x(:)(1:1)) + ichar(x(:)(2:2))) + if ((checksum .ne. 1490) .and. (checksum .ne. 2130)) stop 4 + class default + stop + end select + rank (2) + select type (x) + type is (s) + if (sum(x%i) .ne. 55) stop 5 + if (sum(x%j) .ne. 550) stop 6 + type is (character(*)); + checksum = sum(ichar(x(:,:)(1:1)) + ichar(x(:,:)(2:2))) + if ((checksum .ne. 1490) .and. (checksum .ne. 2130)) stop 7 + class default + stop 8 + end select + rank (3) + select type (x) + type is (s) + if (sum(x%i) .ne. 55) stop 9 + if ((sum(x%j) .ne. 550) .and. (sum(x%j) .ne. 110)) stop 10 + type is (character(*)) + checksum = sum(ichar(x(:,:,:)(1:1)) + ichar(x(:,:,:)(2:2))) + if ((checksum .ne. 1490) .and. (checksum .ne. 2130)) stop 11 + class default + stop 12 + end select + end select + end +end module tests + +Module class_tests + use tests + implicit none + private + public :: test_class + + integer :: j + integer :: src(10) + type (s), allocatable :: src3 (:,:,:) + class(t), allocatable :: B(:,:,:), D(:) + +! gfortran gave type(t) for D for all these test cases. +contains + + subroutine test_class + + src3 = reshape ([(s(j,j*10), j=1,10)], [1,10,1]) + call test1 ! Now D OK for gfc15. B OK back to gfc10 + call foo + + call class_rebar(reshape(B, [10])) ! This is the original failure - run time segfault + + deallocate (B, D) + + allocate(B(2,1,5), source = s(1,11)) ! B was OK but descriptor elem_len = 4 so.... + src = [(j, j=1,10)] + call test2 ! D%j was type(t) and filled with B[1:5] + call foo + deallocate (B,D) + + call test3 ! B is set to type(t) and filled with [s(1,11)..s(5,50)] + call foo + deallocate (B,D) + + B = src3 ! Now D was like B in test3. B OK back to gfc10 + call foo + deallocate (B, D) + if (allocated (src3)) deallocate (src3) + end + + subroutine class_rebar (arg) + class(t) :: arg(:) + call class_bar (arg) + end + + subroutine test1 + allocate(B, source = src3) + end + + subroutine test2 + B%i = RESHAPE(src, shape(B)) + end + + subroutine test3 + B = reshape ([(s(j,j*10), j=1,10)], shape(B)) + end + + subroutine foo + D = reshape(B, [10]) + call class_bar(B) + call class_bar(D) + end +end module class_tests + +module unlimited_tests + use tests + implicit none + private + public :: test_unlimited + + integer :: j + integer :: src(10) + character(len = 2, kind = 1) :: chr(10) + character(len = 2, kind = 1) :: chr3(5, 2, 1) + type (s), allocatable :: src3 (:,:,:) + class(*), allocatable :: B(:,:,:), D(:) + +contains + subroutine test_unlimited + call test1 + call foo + + call unlimited_rebar(reshape(B, [10])) ! Unlimited version of the original failure + + deallocate (B, D) + + call test3 + call foo + deallocate (B,D) + + B = src3 + call foo + deallocate (B, D) + + B = reshape ([(char(64 + 2*j - 1)//char(64 + 2*j), j = 1,10)], [5, 1, 2]) + call foo + deallocate (B, D) + + chr = [(char(96 + 2*j - 1)//char(96 + 2*j), j = 1,10)] + B = reshape (chr, [5, 1, 2]) + call foo + + call unlimited_rebar(reshape(B, [10])) ! Unlimited/ character version of the original failure + + deallocate (B, D) + + chr3 = reshape (chr, shape(chr3)) + B = chr3 + call foo + deallocate (B, D) + if (allocated (src3)) deallocate (src3) + end + + subroutine unlimited_rebar (arg) + class(*) :: arg(:) + call class_bar (arg) + end + + subroutine test1 + src3 = reshape ([(s(j,j*10), j=1,10)], [2,1,5]) + allocate(B, source = src3) + end + + subroutine test3 + B = reshape ([(s(j,j*10), j=1,10)], shape(B)) + end + + subroutine foo + D = reshape(B, [10]) + call class_bar(B) + call class_bar(D) + end + +end module unlimited_tests + + call t1 + call t2 +contains + subroutine t1 + use class_tests + call test_class + end + subroutine t2 + use unlimited_tests + call test_unlimited + end +end diff --git a/gcc/testsuite/gfortran.dg/class_transformational_2.f90 b/gcc/testsuite/gfortran.dg/class_transformational_2.f90 new file mode 100644 index 00000000000..01d04a4700d --- /dev/null +++ b/gcc/testsuite/gfortran.dg/class_transformational_2.f90 @@ -0,0 +1,104 @@ +! { dg-do run } +! +! Test transformational intrinsics other than reshape with class results. +! This emerged from PR102689, for which class_transformational_1.f90 tests +! class-valued reshape. +! +! Contributed by Paul Thomas <pa...@gcc.gnu.org> +! + type t + integer :: i + end type t + type, extends(t) :: s + integer :: j + end type + class(t), allocatable :: scalar, a(:), aa(:), b(:,:), c(:,:,:), field(:,:,:) + integer, allocatable :: ishape(:), ii(:), ij(:) + logical :: la(2), lb(2,2), lc (4,2,2) + integer :: j, stop_flag + + call check_spread + call check_pack + call check_unpack + call check_eoshift + call check_eoshift_dep + deallocate (a, aa, b, c, field, ishape, ii, ij) +contains + subroutine check_result_a (shift) + type (s), allocatable :: ss(:) + integer :: shift + select type (aa) + type is (s) + ss = eoshift (aa, shift = shift, boundary = aa(1), dim = 1) + ishape = shape (aa); + ii = ss%i + ij = ss%j + end select + if (any (ishape .ne. shape (a))) stop stop_flag + 1 + select type (a) + type is (s) + if (any (a%i .ne. ii)) stop stop_flag + 2 + if (any (a%j .ne. ij)) stop stop_flag + 3 + end select + end + + subroutine check_result + if (any (shape (c) .ne. ishape)) stop stop_flag + 1 + select type (a) + type is (s) + if (any (a%i .ne. ii)) stop stop_flag + 2 + if (any (a%j .ne. ij)) stop stop_flag + 3 + end select + end + + subroutine check_spread + stop_flag = 10 + a = [(s(j,10*j), j = 1,2)] + b = spread (a, dim = 2, ncopies = 2) + c = spread (b, dim = 1, ncopies = 4) + a = reshape (c, [size (c)]) + ishape = [4,2,2] + ii = [1,1,1,1,2,2,2,2,1,1,1,1,2,2,2,2] + ij = 10*[1,1,1,1,2,2,2,2,1,1,1,1,2,2,2,2] + call check_result + end + + subroutine check_pack + stop_flag = 20 + la = [.false.,.true.] + lb = spread (la, dim = 2, ncopies = 2) + lc = spread (lb, dim = 1, ncopies = 4) + a = pack (c, mask = lc) + ishape = shape (lc) + ii = [2,2,2,2,2,2,2,2] + ij = 10*[2,2,2,2,2,2,2,2] + call check_result + end + + subroutine check_unpack + stop_flag = 30 + a = [(s(j,10*j), j = 1,16)] + field = reshape ([(s(100*j,1000*j), j = 1,16)], shape(lc)) + c = unpack (a, mask = lc, field = field) + a = reshape (c, [product (shape (lc))]) + ishape = shape (lc) + ii = [100,200,300,400,1,2,3,4,900,1000,1100,1200,5,6,7,8] + ij = [1000,2000,3000,4000,10,20,30,40,9000,10000, 11000,12000,50,60,70,80] + call check_result + end + + subroutine check_eoshift + type (s), allocatable :: ss(:) + stop_flag = 40 + aa = a + a = eoshift (aa, shift = 3, boundary = aa(1), dim = 1) + call check_result_a (3) + end + + subroutine check_eoshift_dep + stop_flag = 50 + aa = a + a = eoshift (a, shift = -3, boundary = a(1), dim = 1) + call check_result_a (-3) + end +end