Hi Peter, thanks for your contribution to gfortran! You've found indeed a solution for a potentially annoying bug.
Am 15.02.24 um 18:50 schrieb Peter Hill:
Dear all, The attached patch fixes PR105658 by forcing an array temporary to be created. This is required when passing an array component, but this didn't happen if the dummy argument was an unlimited polymorphic type. The problem bit of code is in `gfc_conv_expr_descriptor`, near L7828: subref_array_target = (is_subref_array (expr) && (se->direct_byref || expr->ts.type == BT_CHARACTER)); need_tmp = (gfc_ref_needs_temporary_p (expr->ref) && !subref_array_target); where `need_tmp` is being evaluated to 0. The logic here isn't clear to me, and this function is used in several places, which is why I went with setting `parmse.force_tmp = 1` in `gfc_conv_procedure_call` and using the same conditional as the later branch for the non-polymorphic case (near the call to `gfc_conv_subref_array_arg`) If this patch is ok, please could someone commit it for me? This is my first patch for GCC, so apologies in advance if the commit message is missing something.
Your patch mostly does the right thing. Note that when fsym is an unlimited polymorphic, some of its attributes are buried deep within its internal representation. I would also prefer to move the code to gfc_conv_intrinsic_to_class where it seems to fit better, like: diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index a0593b76f18..db906caa52e 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -1019,6 +1019,14 @@ gfc_conv_intrinsic_to_class (gfc_se *parmse, gfc_expr *e, tmp = gfc_typenode_for_spec (&class_ts); var = gfc_create_var (tmp, "class"); + /* Force a temporary for component or substring references. */ + if (unlimited_poly + && class_ts.u.derived->components->attr.dimension + && !class_ts.u.derived->components->attr.class_pointer + && !class_ts.u.derived->components->attr.allocatable + && is_subref_array (e)) + parmse->force_tmp = 1; + /* Set the vptr. */ ctree = gfc_class_vptr_get (var); (I am not entirely sure whether we need to exclude pointer and allocatable attributes here explicitly, given the constraints in F2023:15.5.2.6, but other may have an opinion, too. The above should be safe anyway.)
Tested on x86_64-pc-linux-gnu. The bug is present in gfortran back to 4.9, so should it also be backported?
I think we'll target 14-mainline and might consider a backport to 13-branch.
Cheers, Peter PR fortran/105658 gcc/fortran/ChangeLog * trans-expr.cc (gfc_conv_procedure_call): When passing an array component reference of intrinsic type to a procedure with an unlimited polymorphic dummy argument, a temporary should be created. gcc/testsuite/ChangeLog * gfortran.dg/PR105658.f90: New test. --- gcc/fortran/trans-expr.cc | 8 ++++++++ gcc/testsuite/gfortran.dg/PR105658.f90 | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 gcc/testsuite/gfortran.dg/PR105658.f90 diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index a0593b76f18..7fd3047c4e9 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -6439,6 +6439,14 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, CLASS object for the unlimited polymorphic formal. */ gfc_find_vtab (&e->ts); gfc_init_se (&parmse, se); + /* The actual argument is a component reference to an array + of derived types, so we need to force creation of a + temporary */ + if (e->expr_type == EXPR_VARIABLE + && is_subref_array (e) + && !(fsym && fsym->attr.pointer)) + parmse.force_tmp = 1; + gfc_conv_intrinsic_to_class (&parmse, e, fsym->ts); } diff --git a/gcc/testsuite/gfortran.dg/PR105658.f90 b/gcc/testsuite/gfortran.dg/PR105658.f90 new file mode 100644 index 00000000000..407ee25f77c --- /dev/null +++ b/gcc/testsuite/gfortran.dg/PR105658.f90 @@ -0,0 +1,25 @@ +! { dg-do compile } +! { dg-options "-Warray-temporaries" } +! Test fix for incorrectly passing array component to unlimited polymorphic procedure + +module test_PR105658_mod + implicit none + type :: foo + integer :: member1 + integer :: member2 + end type foo +contains + subroutine print_poly(array) + class(*), dimension(:), intent(in) :: array + select type(array) + type is (integer) + print*, array + end select + end subroutine print_poly + + subroutine do_print(thing) + type(foo), dimension(3), intent(in) :: thing + call print_poly(thing%member1) ! { dg-warning "array temporary" } + end subroutine do_print + +end module test_PR105658_mod
One could extend this testcase to cover substrings as well: module test_PR105658_mod implicit none type :: foo integer :: member1 integer :: member2 end type foo contains subroutine print_poly(array) class(*), dimension(:), intent(in) :: array select type(array) type is (integer) print*, array type is (character(*)) print *, array end select end subroutine print_poly subroutine do_print(thing) type(foo), dimension(3), intent(in) :: thing type(foo), parameter :: y(3) = [foo(1,2),foo(3,4),foo(5,6)] integer :: i, j, uu(5,6) call print_poly(thing%member1) ! { dg-warning "array temporary" } call print_poly(y%member2) ! { dg-warning "array temporary" } call print_poly(y(1::2)%member2) ! { dg-warning "array temporary" } ! The following array sections work without temporaries uu = reshape([(((10*i+j),i=1,5),j=1,6)],[5,6]) print *, uu(2,2::2) call print_poly (uu(2,2::2)) ! no temp needed! print *, uu(1::2,6) call print_poly (uu(1::2,6)) ! no temp needed! end subroutine do_print subroutine do_print2(thing2) class(foo), dimension(:), intent(in) :: thing2 call print_poly (thing2% member2) ! { dg-warning "array temporary" } end subroutine do_print2 subroutine do_print3 () character(3) :: c(3) = ["abc","def","ghi"] call print_poly (c(1::2)) ! no temp needed! call print_poly (c(1::2)(2:3)) ! { dg-warning "array temporary" } end subroutine do_print3 end module test_PR105658_mod If you like, you can repackage the patch and sign it (see https://gcc.gnu.org/dco.html), and one of us will then commit it for you. Thanks! Harald