The following attached patch regression tested on x86_64 and arm.
OK for mainline?
Regards,
Jerry
---
[PATCH] fortran: [PR128478] Copy a parenthesized scalar CLASS actual argument
A parenthesized scalar CLASS variable passed as an actual argument was
not copied, so the dummy aliased the variable, and passing it to a TYPE
dummy with allocatable components segfaulted, because the components of
the CLASS container were copied and freed as if it were the derived
type. Make the value of a parenthesized scalar CLASS variable a copy in
gfc_conv_expr_op, which also serves the ASSOCIATE selector, keep the
free of the copy until after the call, and leave its components to the
copy's own finalization.
Assisted-by: Claude Opus 5.5
PR fortran/128478
gcc/fortran/ChangeLog:
* trans-expr.cc (gfc_conv_expr_op): Copy a parenthesized scalar
CLASS variable.
(gfc_conv_procedure_call): Do not copy or free the allocatable
components of the copy of a parenthesized scalar CLASS variable.
(gfc_conv_expr_reference): Keep the free of that copy in se->post.
* trans-stmt.cc (trans_associate_var): Remove the copy of a
parenthesized CLASS selector, now done by gfc_conv_expr_op.
gcc/testsuite/ChangeLog:
* gfortran.dg/asan/paren_class_copy_1.f90: New test.
---From 83a495cf752741c01879a8f49f5fcbe9cccf2d36 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Fri, 6 Nov 2026 10:33:41 -0800
Subject: [PATCH] fortran: [PR128478] Copy a parenthesized scalar CLASS actual
argument
A parenthesized scalar CLASS variable passed as an actual argument was
not copied, so the dummy aliased the variable, and passing it to a TYPE
dummy with allocatable components segfaulted, because the components of
the CLASS container were copied and freed as if it were the derived
type. Make the value of a parenthesized scalar CLASS variable a copy in
gfc_conv_expr_op, which also serves the ASSOCIATE selector, keep the
free of the copy until after the call, and leave its components to the
copy's own finalization.
Assisted-by: Claude Opus 5.5
PR fortran/128478
gcc/fortran/ChangeLog:
* trans-expr.cc (gfc_conv_expr_op): Copy a parenthesized scalar
CLASS variable.
(gfc_conv_procedure_call): Do not copy or free the allocatable
components of the copy of a parenthesized scalar CLASS variable.
(gfc_conv_expr_reference): Keep the free of that copy in se->post.
* trans-stmt.cc (trans_associate_var): Remove the copy of a
parenthesized CLASS selector, now done by gfc_conv_expr_op.
gcc/testsuite/ChangeLog:
* gfortran.dg/asan/paren_class_copy_1.f90: New test.
---
gcc/fortran/trans-expr.cc | 24 +++++++-
gcc/fortran/trans-stmt.cc | 8 ---
.../gfortran.dg/asan/paren_class_copy_1.f90 | 58 +++++++++++++++++++
3 files changed, 80 insertions(+), 10 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/asan/paren_class_copy_1.f90
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index d41c3af108f..8e17ff60de2 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -4204,6 +4204,14 @@ gfc_conv_expr_op (gfc_se * se, gfc_expr * expr)
switch (expr->value.op.op)
{
case INTRINSIC_PARENTHESES:
+ /* The value of a parenthesized CLASS variable is a copy. */
+ if (expr->ts.type == BT_CLASS && expr->rank == 0
+ && parenthesized_variable (expr))
+ {
+ gfc_conv_expr (se, expr->value.op.op1);
+ gfc_conv_class_value_copy (se);
+ return;
+ }
if ((expr->ts.type == BT_REAL || expr->ts.type == BT_COMPLEX)
&& flag_protect_parens)
{
@@ -8927,12 +8935,14 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
procedures. As bounds information for descriptorless arrays is no
longer available here, they are dealt with in trans-array.cc
(gfc_conv_array_parameter). A derived-type temporary for a
- parenthesized variable shares the components of the variable. */
+ parenthesized variable shares the components of the variable, and
+ the copy of a parenthesized scalar CLASS variable frees its own. */
if (e && (e->ts.type == BT_DERIVED || e->ts.type == BT_CLASS)
&& e->ts.u.derived->attr.alloc_comp
&& (e->rank == 0 || elemental_proc || !nodesc_arg)
&& !expr_may_alias_variables (e, elemental_proc)
- && !(e->ts.type == BT_DERIVED && parenthesized_variable (e)))
+ && !((e->ts.type == BT_DERIVED || e->rank == 0)
+ && parenthesized_variable (e)))
{
int parm_rank;
/* It is known the e returns a structure type with at least one
@@ -11754,6 +11764,16 @@ gfc_conv_expr_reference (gfc_se * se, gfc_expr * expr)
return;
}
+ /* The copy of a parenthesized CLASS variable is freed in se->post, so it
+ must stay there until after the copy's use. */
+ if (expr->ts.type == BT_CLASS && expr->rank == 0
+ && parenthesized_variable (expr))
+ {
+ gfc_conv_expr (se, expr);
+ se->expr = gfc_build_addr_expr (NULL_TREE, se->expr);
+ return;
+ }
+
if (expr->expr_type == EXPR_FUNCTION
&& ((expr->value.function.esym
&& expr->value.function.esym->result
diff --git a/gcc/fortran/trans-stmt.cc b/gcc/fortran/trans-stmt.cc
index 0c080f59120..9083cd5edd2 100644
--- a/gcc/fortran/trans-stmt.cc
+++ b/gcc/fortran/trans-stmt.cc
@@ -2376,14 +2376,6 @@ trans_associate_var (gfc_symbol *sym, gfc_wrapped_block *block)
gfc_add_block_to_block (&se.post, &se.finalblock);
need_len_assign = false;
}
- else if (sym->ts.type == BT_CLASS && e->expr_type == EXPR_OP
- && e->value.op.op == INTRINSIC_PARENTHESES && e->rank == 0)
- {
- /* A parenthesized selector is associated with a copy. */
- gfc_conv_expr (&se, e);
- gfc_conv_class_value_copy (&se);
- need_len_assign = false;
- }
else if (sym->ts.type == BT_CLASS && CLASS_DATA (sym)->attr.dimension)
{
tree target_expr;
diff --git a/gcc/testsuite/gfortran.dg/asan/paren_class_copy_1.f90 b/gcc/testsuite/gfortran.dg/asan/paren_class_copy_1.f90
new file mode 100644
index 00000000000..5689691d808
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/asan/paren_class_copy_1.f90
@@ -0,0 +1,58 @@
+! { dg-do run }
+!
+! PR 128478
+! A parenthesized scalar CLASS variable passed as an actual argument is a
+! copy; without the fix the dummy aliased the variable, and a TYPE dummy
+! segfaulted. Checked under -fsanitize=address.
+
+module m
+ implicit none
+ type :: t
+ integer, allocatable :: v(:)
+ end type
+contains
+ subroutine sq (x, z)
+ class(*), intent(in) :: x
+ complex, intent(inout) :: z
+ z = (0., 0.)
+ select type (x)
+ type is (complex)
+ if (x /= (5., 6.)) stop 1
+ class default
+ stop 2
+ end select
+ end subroutine
+ subroutine st (x, z)
+ class(t), intent(in) :: x
+ type(t), intent(inout) :: z
+ z%v = [0, 0]
+ if (any (x%v /= [5, 6])) stop 3
+ end subroutine
+ subroutine s1 (x)
+ type(t), intent(in) :: x
+ if (any (x%v /= [5, 6])) stop 6
+ end subroutine
+end module
+
+program p
+ use m
+ implicit none
+ complex, target :: zs = (5., 6.)
+ type(t), target :: ts
+ class(*), pointer :: q
+ class(t), pointer :: qt
+ class(t), allocatable :: cs
+
+ q => zs
+ call sq ((q), zs)
+ if (zs /= (0., 0.)) stop 4
+
+ ts = t ([5, 6])
+ qt => ts
+ call st ((qt), ts)
+ if (any (ts%v /= 0)) stop 5
+
+ allocate (cs, source = t ([5, 6]))
+ call s1 ((cs))
+ if (any (cs%v /= [5, 6])) stop 7
+end program
--
2.56.0