https://gcc.gnu.org/g:ee65440cbd8042a5e5885e18bde70f8d530e4404
commit r15-9328-gee65440cbd8042a5e5885e18bde70f8d530e4404 Author: Paul Thomas <pa...@gcc.gnu.org> Date: Wed Apr 9 09:50:04 2025 +0100 Fortran: Fix some problems with the reduce intrinsic [PR119460] 2025-04-09 Paul Thomas <pa...@gcc.gnu.org> and Harald Anlauf <anl...@gcc.gnu.org> gcc/fortran PR fortran/119460 * iresolve.cc (generate_reduce_op_wrapper): Increase the size of 'tname'. Change intent of 'a' and 'b' to intent_in. * trans-decl.cc (add_argument_checking): Do not test artificial formal symbols. * trans-expr.cc (gfc_conv_procedure_call): Remove reduce_scalar and the blocks triggered by it. * trans-intrinsic.cc (gfc_conv_intrinsic_function): Set the result of non-character, scalar reduce to be allocatable. gcc/testsuite/ PR fortran/119460 * gfortran.dg/reduce_2.f90: Add test to check that deferred len characters cannot slip through. * gfortran.dg/reduce_3.f90: New test * gfortran.dg/reduce_4.f90: New test libgfortran/ PR libfortran/119460 * intrinsics/reduce.c (reduce): Correct error message about mismatch between dim and the rank of array. Output the values of both. Correct the evaluation of the result stride and extent. (reduce_scalar): The front end treats the result as an allocatable so eliminate memcpy and free. Return the base-addr of the local descriptor. (reduce_c): Correct the type of the string lengths. (reduce_scalar_c): Correct the type of the string lengths.Test to see if 'res' is allocated. If not then return the base_addr of the local descriptor. Diff: --- gcc/fortran/iresolve.cc | 6 +-- gcc/fortran/trans-decl.cc | 2 +- gcc/fortran/trans-expr.cc | 24 ----------- gcc/fortran/trans-intrinsic.cc | 7 ++++ gcc/testsuite/gfortran.dg/reduce_2.f90 | 8 ++++ gcc/testsuite/gfortran.dg/reduce_3.f90 | 56 +++++++++++++++++++++++++ gcc/testsuite/gfortran.dg/reduce_4.f90 | 48 +++++++++++++++++++++ libgfortran/intrinsics/reduce.c | 77 ++++++++++++++++++++-------------- 8 files changed, 168 insertions(+), 60 deletions(-) diff --git a/gcc/fortran/iresolve.cc b/gcc/fortran/iresolve.cc index 8189d7a1c6f6..858ffb1daebf 100644 --- a/gcc/fortran/iresolve.cc +++ b/gcc/fortran/iresolve.cc @@ -2417,7 +2417,7 @@ generate_reduce_op_wrapper (gfc_expr *op) gfc_symbol *operation = op->symtree->n.sym; gfc_symbol *wrapper, *a, *b, *c; gfc_symtree *st; - char tname[GFC_MAX_SYMBOL_LEN+1]; + char tname[2 * GFC_MAX_SYMBOL_LEN + 2]; char *name; gfc_namespace *ns; gfc_expr *e; @@ -2462,7 +2462,7 @@ generate_reduce_op_wrapper (gfc_expr *op) a->attr.flavor = FL_VARIABLE; a->attr.dummy = 1; a->attr.artificial = 1; - a->attr.intent = INTENT_INOUT; + a->attr.intent = INTENT_IN; wrapper->formal = gfc_get_formal_arglist (); wrapper->formal->sym = a; gfc_set_sym_referenced (a); @@ -2476,7 +2476,7 @@ generate_reduce_op_wrapper (gfc_expr *op) b->attr.dummy = 1; b->attr.optional= 1; b->attr.artificial = 1; - b->attr.intent = INTENT_INOUT; + b->attr.intent = INTENT_IN; wrapper->formal->next = gfc_get_formal_arglist (); wrapper->formal->next->sym = b; gfc_set_sym_referenced (b); diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc index 9087221dabbf..aea132ded13c 100644 --- a/gcc/fortran/trans-decl.cc +++ b/gcc/fortran/trans-decl.cc @@ -6546,7 +6546,7 @@ add_argument_checking (stmtblock_t *block, gfc_symbol *sym) message = _("Actual string length does not match the declared one" " for dummy argument '%s' (%ld/%ld)"); } - else if (fsym->as && fsym->as->rank != 0) + else if ((fsym->as && fsym->as->rank != 0) || fsym->attr.artificial) continue; else { diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index 4b90b06fa0a0..6ece39b218d0 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -6753,12 +6753,6 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, gfc_intrinsic_sym *isym = expr && expr->rank ? expr->value.function.isym : NULL; - /* In order that the library function for intrinsic REDUCE be type and kind - agnostic, the result is passed by reference. Allocatable components are - handled within the OPERATION wrapper. */ - bool reduce_scalar = expr && !expr->rank && expr->value.function.isym - && expr->value.function.isym->id == GFC_ISYM_REDUCE; - comp = gfc_get_proc_ptr_comp (expr); bool elemental_proc = (comp @@ -8596,16 +8590,6 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else if (ts.type == BT_CHARACTER) vec_safe_push (retargs, len); } - else if (reduce_scalar) - { - /* In order that the library function for intrinsic REDUCE be type and - kind agnostic, the result is passed by reference. Allocatable - components are handled within the OPERATION wrapper. */ - type = gfc_typenode_for_spec (&expr->ts); - result = gfc_create_var (type, "sr"); - tmp = gfc_build_addr_expr (pvoid_type_node, result); - vec_safe_push (retargs, tmp); - } gfc_free_interface_mapping (&mapping); @@ -8821,14 +8805,6 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, gfc_add_expr_to_block (&se->pre, tmp); } } - else if (reduce_scalar) - { - /* Even though the REDUCE intrinsic library function returns the result - by reference, the scalar call passes the result as se->expr. */ - gfc_add_expr_to_block (&se->pre, se->expr); - se->expr = result; - gfc_add_block_to_block (&se->post, &post); - } else { /* For a function with a class array result, save the result as diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc index 6b55017bb897..6ffc3e0261e5 100644 --- a/gcc/fortran/trans-intrinsic.cc +++ b/gcc/fortran/trans-intrinsic.cc @@ -3883,6 +3883,13 @@ gfc_conv_intrinsic_funcall (gfc_se * se, gfc_expr * expr) append_args->quick_push (null_pointer_node); } } + /* Non-character scalar reduce returns a pointer to a result of size set by + the element size of 'array'. Setting 'sym' allocatable ensures that the + result is deallocated at the appropriate time. */ + else if (expr->value.function.isym->id == GFC_ISYM_REDUCE + && expr->rank == 0 && expr->ts.type != BT_CHARACTER) + sym->attr.allocatable = 1; + gfc_conv_procedure_call (se, sym, expr->value.function.actual, expr, append_args); diff --git a/gcc/testsuite/gfortran.dg/reduce_2.f90 b/gcc/testsuite/gfortran.dg/reduce_2.f90 index 52d7c682a853..cacd54a5b608 100644 --- a/gcc/testsuite/gfortran.dg/reduce_2.f90 +++ b/gcc/testsuite/gfortran.dg/reduce_2.f90 @@ -8,6 +8,10 @@ integer, allocatable :: i(:,:,:) integer :: n(2,2) Logical :: l1(4), l2(2,3), l3(2,2) + type :: string_t + character(:), allocatable :: chr(:) + end type + type(string_t) :: str ! The ARRAY argument at (1) of REDUCE shall not be polymorphic print *, reduce (cstar, add) ! { dg-error "shall not be polymorphic" } @@ -54,6 +58,10 @@ ! (2) shall be the same print *, reduce ([character(4) :: 'abcd','efgh'], char_three) ! { dg-error "arguments of the OPERATION" } +! The character length of the ARRAY argument at (1) and of the arguments of the OPERATION at (2) +! shall be the same + str = reduce ([character(4) :: 'abcd','efgh'], char_one) ! { dg-error "character length of the ARRAY" } + ! The DIM argument at (1), if present, must be an integer scalar print *, reduce (i, add, dim = 2.0) ! { dg-error "must be an integer scalar" } diff --git a/gcc/testsuite/gfortran.dg/reduce_3.f90 b/gcc/testsuite/gfortran.dg/reduce_3.f90 new file mode 100644 index 000000000000..c0ed0623e49e --- /dev/null +++ b/gcc/testsuite/gfortran.dg/reduce_3.f90 @@ -0,0 +1,56 @@ +! { dg-do run } +! +! PR119460: Scalar reduce was failing with ARRAY elements larger than +! an address size. +! +! Contributed by Rainer Orth <r...@gcc.gnu.org> +! +program test_reduce + implicit none + integer :: i + integer, parameter :: dp = kind(1.0_8), extent = 4 + + real(dp) :: rarray(extent,extent,extent), rmat(extent,extent), & + rvec (extent), rscl + + type :: t + real(dp) :: field(extent) + end type t + + type (t) :: tmat(extent, extent), tarray(extent), tscalar + + rarray = reshape ([(real(i, kind = dp), i = 1, size(rarray))], & + shape (rarray)) + + rmat = reduce (rarray, add, dim = 1) + if (any (rmat /= sum (rarray, 1))) stop 1 + + rmat = reduce (rarray, add, dim = 2) + if (any (rmat /= sum (rarray, 2))) stop 2 + + rmat = reduce (rarray, add, dim = 3) + if (any (rmat /= sum (rarray, 3))) stop 3 + + rscl = reduce (rarray, add) + if (rscl /= sum (rarray)) stop 4 + + tmat%field(1) = rmat + tarray = reduce (tmat, t_add, dim =1) + rvec = reduce (rmat, add, dim = 1) + if (any (tarray%field(1) /= rvec)) stop 5 + + tscalar = reduce (tmat, t_add) + if (tscalar%field(1) /= sum (tmat%field(1))) stop 6 +contains + + pure real(dp) function add (i, j) + real(dp), intent(in) :: i, j + add = i + j + end function add + + pure type(t) function t_add (i, j) + type(t), intent(in) :: i, j + t_add%field(1) = i%field(1) + j%field(1) + end function t_add + +end diff --git a/gcc/testsuite/gfortran.dg/reduce_4.f90 b/gcc/testsuite/gfortran.dg/reduce_4.f90 new file mode 100644 index 000000000000..edea93166c33 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/reduce_4.f90 @@ -0,0 +1,48 @@ +! { dg-do run } +! +! PR119540 comment2: REDUCE was getting the shape wrong. This testcase also +! verifies that the longest possible name for the OPERATION wrapper function +! is catered for. +! +! Contributed by Harald Anlauf <anl...@gcc.gnu.org> +! +program p2345678901234567890123456789012345678901234567890123456789_123 + implicit none + integer, parameter :: n = 3 + integer, parameter :: vec(n) = [2, 5, 10] + integer, parameter :: mat(n,2) = reshape([vec,2*vec],[n,2]) + integer :: mat_shape(2), reduce_shape(1), r + integer, dimension(:), allocatable :: res1 + + mat_shape = shape (mat) + reduce_shape = shape (reduce (mat, add, 1), 1) + if (reduce_shape(1) /= mat_shape(2)) stop 1 + + reduce_shape = shape (reduce (mat, add, 1), 1) + if (reduce_shape(1) /= mat_shape(2)) stop 2 + + res1 = reduce (mat, add, 1) + if (any (res1 /= [17, 34])) stop 3 + + res1 = reduce (mat, add, 2) + if (any (res1 /= [6, 15, 30])) stop 4 + + r = reduce (vec, & + o2345678901234567890123456789012345678901234567890123456789_123) + if (r /= 17) stop 5 + + deallocate (res1) +contains + pure function add(i,j) result(sum_ij) + integer, intent(in) :: i, j + integer :: sum_ij + sum_ij = i + j + end function add + + pure function o2345678901234567890123456789012345678901234567890123456789_123 (i, j) & + result (sum) + integer, intent(in) :: i, j + integer :: sum + sum = i + j + end function +end diff --git a/libgfortran/intrinsics/reduce.c b/libgfortran/intrinsics/reduce.c index c8950e41fd01..256394f08501 100644 --- a/libgfortran/intrinsics/reduce.c +++ b/libgfortran/intrinsics/reduce.c @@ -52,14 +52,14 @@ reduce (parray *ret, index_type ext0, ext1, ext2; index_type str0, str1, str2; index_type idx0, idx1, idx2; - index_type dimen, dimen_m1, ldx; + index_type dimen, dimen_m1, ldx, ext, str; bool started; bool masked = false; bool dim_present = dim != NULL; bool mask_present = mask != NULL; bool identity_present = identity != NULL; bool scalar_result; - int i; + int i, j; int array_rank = (int)GFC_DESCRIPTOR_RANK (array); size_t elem_len = GFC_DESCRIPTOR_SIZE (array); @@ -83,8 +83,8 @@ reduce (parray *ret, if (dim_present) { if ((*dim < 1) || (*dim > (GFC_INTEGER_4)array_rank)) - runtime_error ("DIM in REDUCE intrinsic is less than 0 or greater than " - "the rank of ARRAY"); + runtime_error ("Mismatch between DIM and the rank of ARRAY in the " + "REDUCE intrinsic (%d/%d)", (int)*dim, array_rank); dimen = (index_type) *dim; } else @@ -99,33 +99,39 @@ reduce (parray *ret, scalar_result = (!dim_present && array_rank > 1) || array_rank == 1; + j = 0; for (i = 0; i < array_rank; i++) { /* Obtain the shape of the reshaped ARRAY. */ - index_type ext = GFC_DESCRIPTOR_EXTENT (array,i); - index_type str = GFC_DESCRIPTOR_STRIDE (array,i); + ext = GFC_DESCRIPTOR_EXTENT (array,i); + str = GFC_DESCRIPTOR_STRIDE (array,i); if (masked && (ext != GFC_DESCRIPTOR_EXTENT (mask, i))) - runtime_error ("shape mismatch between ARRAY and MASK in REDUCE " - "intrinsic"); + { + int mext = (int)GFC_DESCRIPTOR_EXTENT (mask, i); + runtime_error ("shape mismatch between ARRAY and MASK in the REDUCE " + "intrinsic (%zd/%d)", ext, mext); + } if (scalar_result) { ext1 *= ext; continue; } - else if (i < dimen_m1) + else if (i < (int)dimen_m1) ext0 *= ext; - else if (i == dimen_m1) + else if (i == (int)dimen_m1) ext1 = ext; else ext2 *= ext; /* The dimensions of the return array. */ - if (i < (int)(dimen - 1)) - GFC_DIMENSION_SET (ret->dim[i], 0, ext - 1, str); - else if (i < array_rank - 1) - GFC_DIMENSION_SET (ret->dim[i], 0, ext - 1, str); + if (i != (int)dimen_m1) + { + str = GFC_DESCRIPTOR_STRIDE (array, j); + GFC_DIMENSION_SET (ret->dim[j], 0, ext - 1, str); + j++; + } } if (!scalar_result) @@ -214,14 +220,13 @@ reduce (parray *ret, } -extern void reduce_scalar (void *, parray *, +extern void * reduce_scalar (parray *, void (*operation) (void *, void *, void *), GFC_INTEGER_4 *, gfc_array_l4 *, void *, void *); export_proto (reduce_scalar); -void -reduce_scalar (void *res, - parray *array, +void * +reduce_scalar (parray *array, void (*operation) (void *, void *, void *), GFC_INTEGER_4 *dim, gfc_array_l4 *mask, @@ -232,55 +237,63 @@ reduce_scalar (void *res, ret.base_addr = NULL; ret.dtype.rank = 0; reduce (&ret, array, operation, dim, mask, identity, ordered); - memcpy (res, ret.base_addr, GFC_DESCRIPTOR_SIZE (array)); - if (ret.base_addr) free (ret.base_addr); + return (void *)ret.base_addr; } -extern void reduce_c (parray *, index_type, parray *, +extern void reduce_c (parray *, gfc_charlen_type, parray *, void (*operation) (void *, void *, void *), GFC_INTEGER_4 *, gfc_array_l4 *, void *, void *, - index_type, index_type); + gfc_charlen_type, gfc_charlen_type); export_proto (reduce_c); void reduce_c (parray *ret, - index_type ret_strlen __attribute__ ((unused)), + gfc_charlen_type ret_strlen __attribute__ ((unused)), parray *array, void (*operation) (void *, void *, void *), GFC_INTEGER_4 *dim, gfc_array_l4 *mask, void *identity, void *ordered, - index_type array_strlen __attribute__ ((unused)), - index_type identity_strlen __attribute__ ((unused))) + gfc_charlen_type array_strlen __attribute__ ((unused)), + gfc_charlen_type identity_strlen __attribute__ ((unused))) { + /* The frontend constraints make string length checking redundant. Also, the + scalar symbol is flagged to be allocatable in trans-intrinsic.cc so that + gfc_conv_procedure_call does the necessary allocation/deallocation. */ reduce (ret, array, operation, dim, mask, identity, ordered); } -extern void reduce_scalar_c (void *, index_type, parray *, +extern void reduce_scalar_c (void *, gfc_charlen_type, parray *, void (*operation) (void *, void *, void *), GFC_INTEGER_4 *, gfc_array_l4 *, void *, void *, - index_type, index_type); + gfc_charlen_type, gfc_charlen_type); export_proto (reduce_scalar_c); void reduce_scalar_c (void *res, - index_type res_strlen __attribute__ ((unused)), + gfc_charlen_type res_strlen __attribute__ ((unused)), parray *array, void (*operation) (void *, void *, void *), GFC_INTEGER_4 *dim, gfc_array_l4 *mask, void *identity, void *ordered, - index_type array_strlen __attribute__ ((unused)), - index_type identity_strlen __attribute__ ((unused))) + gfc_charlen_type array_strlen __attribute__ ((unused)), + gfc_charlen_type identity_strlen __attribute__ ((unused))) { parray ret; ret.base_addr = NULL; ret.dtype.rank = 0; + /* The frontend constraints make string length checking redundant. */ reduce (&ret, array, operation, dim, mask, identity, ordered); - memcpy (res, ret.base_addr, GFC_DESCRIPTOR_SIZE (array)); - if (ret.base_addr) free (ret.base_addr); + if (res) + { + memcpy (res, ret.base_addr, GFC_DESCRIPTOR_SIZE (array)); + if (ret.base_addr) free (ret.base_addr); + } + else + res = ret.base_addr; }