See the attached patch. I ran into this bug while testing for another unrelated
PR.
Regression tested on x86_64 at -m64 and -m32
OK for mainline?
Regards,
Jerry
---
[PATCH] fortran: [PR128678] Fix OpenMP ctor/dtor for SIMD body-local decls
Gimplify gives an addressable decl local to a SIMD body an implicit
private clause. The Fortran clause hooks treated it like a user's
private variable: the default constructor hit gcc_assert (outer) for a
decl with allocatable components, and the destructor freed the data of
a CLASS temporary, which for a non-polymorphic actual argument is the
actual itself. The body initializes and finalizes such decls, so both
hooks now do nothing for a private clause without an outer reference on
a decl that gfc_omp_private_outer_ref accepts. The destructor also no
longer frees the data of a nonallocatable CLASS copy that
gfc_omp_clause_copy_ctor did not allocate.
Assisted-by: Claude Opus 5.5
PR fortran/128678
gcc/fortran/ChangeLog:
* trans-openmp.cc (gfc_is_class_pointer_type): Rename to ...
(gfc_class_container_kind): ... this. Return the kind suffix of
the class container type name.
(gfc_omp_body_local_private_p): New function.
(gfc_omp_clause_default_ctor): Return NULL_TREE for a body-local
private clause.
(gfc_omp_clause_copy_ctor): Use gfc_class_container_kind.
(gfc_omp_clause_dtor): Likewise. Return NULL_TREE for a body-local
private clause and for a nonallocatable CLASS copy not made by
gfc_omp_clause_copy_ctor.
gcc/testsuite/ChangeLog:
* gfortran.dg/gomp/simd-body-local-1.f90: New test.
libgomp/ChangeLog:
* testsuite/libgomp.fortran/simd-body-local-1.f90: New test.
---From c297c3861a7cb39c2923dc0d5378deb6a1ff8862 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Thu, 12 Nov 2026 10:28:47 -0800
Subject: [PATCH] fortran: [PR128678] Fix OpenMP ctor/dtor for SIMD body-local
decls
Gimplify gives an addressable decl local to a SIMD body an implicit
private clause. The Fortran clause hooks treated it like a user's
private variable: the default constructor hit gcc_assert (outer) for a
decl with allocatable components, and the destructor freed the data of
a CLASS temporary, which for a non-polymorphic actual argument is the
actual itself. The body initializes and finalizes such decls, so both
hooks now do nothing for a private clause without an outer reference on
a decl that gfc_omp_private_outer_ref accepts. The destructor also no
longer frees the data of a nonallocatable CLASS copy that
gfc_omp_clause_copy_ctor did not allocate.
Assisted-by: Claude Opus 5.5
PR fortran/128678
gcc/fortran/ChangeLog:
* trans-openmp.cc (gfc_is_class_pointer_type): Rename to ...
(gfc_class_container_kind): ... this. Return the kind suffix of
the class container type name.
(gfc_omp_body_local_private_p): New function.
(gfc_omp_clause_default_ctor): Return NULL_TREE for a body-local
private clause.
(gfc_omp_clause_copy_ctor): Use gfc_class_container_kind.
(gfc_omp_clause_dtor): Likewise. Return NULL_TREE for a body-local
private clause and for a nonallocatable CLASS copy not made by
gfc_omp_clause_copy_ctor.
gcc/testsuite/ChangeLog:
* gfortran.dg/gomp/simd-body-local-1.f90: New test.
libgomp/ChangeLog:
* testsuite/libgomp.fortran/simd-body-local-1.f90: New test.
---
gcc/fortran/trans-openmp.cc | 41 +++++++++---
.../gfortran.dg/gomp/simd-body-local-1.f90 | 66 +++++++++++++++++++
.../libgomp.fortran/simd-body-local-1.f90 | 60 +++++++++++++++++
3 files changed, 159 insertions(+), 8 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/gomp/simd-body-local-1.f90
create mode 100644 libgomp/testsuite/libgomp.fortran/simd-body-local-1.f90
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index d511c267de9..13b17e573db 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -494,10 +494,11 @@ gfc_is_polymorphic_nonptr (tree type)
return GFC_CLASS_TYPE_P (type);
}
-/* Return true if TYPE is a class container for a POINTER entity. */
+/* If TYPE is a class container, return 'p', 'a' or 't' for a POINTER,
+ ALLOCATABLE or other entity (cf. gfc_build_class_symbol), else 0. */
-static bool
-gfc_is_class_pointer_type (tree type)
+static char
+gfc_class_container_kind (tree type)
{
tree name;
const char *s;
@@ -506,16 +507,16 @@ gfc_is_class_pointer_type (tree type)
type = TREE_TYPE (type);
if (!GFC_CLASS_TYPE_P (type))
- return false;
+ return 0;
name = TYPE_NAME (type);
if (name && TREE_CODE (name) == TYPE_DECL)
name = DECL_NAME (name);
if (!name)
- return false;
+ return 0;
s = IDENTIFIER_POINTER (name);
- return startswith (s, "__class_") && s[strlen (s) - 1] == 'p';
+ return startswith (s, "__class_") ? s[strlen (s) - 1] : 0;
}
/* Return true if TYPE is unlimited polymorphic but not with pointer attribute;
@@ -586,6 +587,19 @@ gfc_omp_private_outer_ref (tree decl)
return false;
}
+/* Return true if CLAUSE privatizes a decl that gfc_omp_private_outer_ref
+ accepts, but without an outer reference: gimplify adds such a clause for
+ an addressable decl local to a SIMD body, which the body itself
+ initializes and finalizes. */
+
+static bool
+gfc_omp_body_local_private_p (tree clause)
+{
+ return (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_PRIVATE
+ && !OMP_CLAUSE_PRIVATE_OUTER_REF (clause)
+ && gfc_omp_private_outer_ref (OMP_CLAUSE_DECL (clause)));
+}
+
/* Callback for gfc_omp_unshare_expr. */
static tree
@@ -839,6 +853,9 @@ gfc_omp_clause_default_ctor (tree clause, tree decl, tree outer)
gcc_unreachable ();
}
+ if (gfc_omp_body_local_private_p (clause))
+ return NULL_TREE;
+
if ((! GFC_DESCRIPTOR_TYPE_P (type)
|| GFC_TYPE_ARRAY_AKIND (type) != GFC_ARRAY_ALLOCATABLE)
&& (!GFC_DECL_GET_SCALAR_ALLOCATABLE (OMP_CLAUSE_DECL (clause))
@@ -971,7 +988,7 @@ gfc_omp_clause_copy_ctor (tree clause, tree dest, tree src)
if (DECL_P (orig_decl)
&& (GFC_DECL_ASSOCIATE_VAR_P (orig_decl)
|| GFC_DECL_GET_SCALAR_POINTER (orig_decl)
- || gfc_is_class_pointer_type (decl_type)))
+ || gfc_class_container_kind (decl_type) == 'p'))
return build2 (MODIFY_EXPR, TREE_TYPE (dest), dest, src);
if (gfc_is_polymorphic_nonptr (decl_type))
@@ -1495,7 +1512,15 @@ gfc_omp_clause_dtor (tree clause, tree decl)
if (DECL_P (orig_decl)
&& (GFC_DECL_ASSOCIATE_VAR_P (orig_decl)
|| GFC_DECL_GET_SCALAR_POINTER (orig_decl)
- || gfc_is_class_pointer_type (decl_type)))
+ || gfc_class_container_kind (decl_type) == 'p'))
+ return NULL_TREE;
+ if (gfc_omp_body_local_private_p (clause))
+ return NULL_TREE;
+ /* Only gfc_omp_clause_copy_ctor gives a nonallocatable CLASS copy data of
+ its own. */
+ if (gfc_class_container_kind (decl_type) == 't'
+ && OMP_CLAUSE_CODE (clause) != OMP_CLAUSE_FIRSTPRIVATE
+ && OMP_CLAUSE_CODE (clause) != OMP_CLAUSE_LINEAR)
return NULL_TREE;
if (gfc_is_polymorphic_nonptr (decl_type))
{
diff --git a/gcc/testsuite/gfortran.dg/gomp/simd-body-local-1.f90 b/gcc/testsuite/gfortran.dg/gomp/simd-body-local-1.f90
new file mode 100644
index 00000000000..69f0438cfa1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/simd-body-local-1.f90
@@ -0,0 +1,66 @@
+! { dg-do compile }
+! PR 128678
+!
+! Addressable decls local to a SIMD body get an implicit private clause;
+! the body initializes and finalizes them, so the OpenMP constructor hook
+! must not. This used to ICE in gfc_omp_clause_default_ctor.
+
+module m
+ implicit none
+ type t
+ real, allocatable :: v(:)
+ end type
+ type pair
+ type(t) :: i, o
+ end type
+ type batch
+ type(pair), allocatable :: p(:)
+ end type
+contains
+ elemental function mkpair (i, o) result (r)
+ type(t), intent(in) :: i, o
+ type(pair) :: r
+ r%i = i
+ r%o = o
+ end function
+ pure function mkbatch (p) result (r)
+ type(pair), intent(in) :: p(:)
+ type(batch) :: r
+ r%p = p
+ end function
+end module
+
+subroutine temps (a, b)
+ use m
+ implicit none
+ type(t) :: a(4, 3)
+ type(batch) :: b(3)
+ integer :: j
+ !$omp parallel do simd
+ do j = 1, 3
+ b(j) = mkbatch (mkpair (a(:,j), a(:,j)))
+ end do
+end subroutine
+
+subroutine block_locals (r)
+ use m
+ implicit none
+ real :: r(100)
+ integer :: j
+ !$omp simd
+ do j = 1, 100
+ block
+ real, allocatable :: w(:)
+ type(t) :: x
+ allocate (w(j))
+ w = 1.0
+ x%v = w
+ call use_it (x%v, w)
+ r(j) = sum (x%v)
+ end block
+ end do
+contains
+ subroutine use_it (a, b)
+ real :: a(:), b(:)
+ end subroutine
+end subroutine
diff --git a/libgomp/testsuite/libgomp.fortran/simd-body-local-1.f90 b/libgomp/testsuite/libgomp.fortran/simd-body-local-1.f90
new file mode 100644
index 00000000000..94ed5500090
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/simd-body-local-1.f90
@@ -0,0 +1,60 @@
+! { dg-do run }
+! PR 128678
+!
+! Decls local to a SIMD body that own allocated memory, and CLASS
+! temporaries there, must not be freed by the OpenMP destructor hook: a
+! CLASS temporary for a non-polymorphic actual points to that actual.
+
+module m
+ implicit none
+ type t
+ real, allocatable :: v(:)
+ end type
+ type acc
+ integer :: base = 10
+ end type
+ type, extends(acc) :: acc2
+ integer :: extra = 1
+ end type
+contains
+ pure integer function bump (this, k)
+ class(acc), intent(in) :: this
+ integer, intent(in) :: k
+ bump = this%base + k
+ end function
+end module
+
+program p
+ use m
+ implicit none
+ type(acc) :: o
+ type(acc2) :: o2
+ integer :: r(50), s(50), i
+ real :: x(50)
+
+ !$omp parallel do simd
+ do i = 1, 50
+ r(i) = bump (o, i)
+ s(i) = bump (o2, -i)
+ end do
+
+ !$omp parallel do simd
+ do i = 1, 50
+ block
+ real, allocatable :: w(:)
+ type(t) :: y
+ class(acc), allocatable :: c
+ allocate (w(i))
+ w = 1.0
+ y%v = w
+ allocate (acc2 :: c)
+ c%base = i
+ x(i) = sum (y%v) + c%base
+ end block
+ end do
+
+ do i = 1, 50
+ if (r(i) /= 10 + i .or. s(i) /= 10 - i) stop 1
+ if (x(i) /= 2 * i) stop 2
+ end do
+end program
--
2.56.0