https://gcc.gnu.org/g:e33257cab75c1f8a07ea8d5c829b8aec7069683e
commit r13-9254-ge33257cab75c1f8a07ea8d5c829b8aec7069683e Author: Paul Thomas <pa...@gcc.gnu.org> Date: Sun Dec 15 14:48:59 2024 +0000 Fortran: Fix non_overridable typebound proc problems [PR84674/117730]. 2024-12-15 Paul Thomas <pa...@gcc.gnu.org> gcc/fortran/ChangeLog PR fortran/117730 PR fortran/84674 * class.cc (add_proc_comp): If the present typebound procedure component is abstract, unconditionally check the replacement. Only reject a non_overridable if it has no overridden procedure and the component is already present in the vtype. gcc/testsuite/ChangeLog PR fortran/117730 * gfortran.dg/pr117730_a.f90: New test. * gfortran.dg/pr117730_b.f90: New test. PR fortran/84674 * gfortran.dg/pr84674.f90: New test. (cherry picked from commit 1572e634dec4a09593f68645939b5b5043de8de6) Diff: --- gcc/fortran/class.cc | 17 ++++++++-- gcc/testsuite/gfortran.dg/pr117730_a.f90 | 50 +++++++++++++++++++++++++++++ gcc/testsuite/gfortran.dg/pr117730_b.f90 | 47 +++++++++++++++++++++++++++ gcc/testsuite/gfortran.dg/pr84674.f90 | 55 ++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 3 deletions(-) diff --git a/gcc/fortran/class.cc b/gcc/fortran/class.cc index ddf840761605..1ef7af09737e 100644 --- a/gcc/fortran/class.cc +++ b/gcc/fortran/class.cc @@ -818,12 +818,23 @@ static void add_proc_comp (gfc_symbol *vtype, const char *name, gfc_typebound_proc *tb) { gfc_component *c; - - if (tb->non_overridable && !tb->overridden) - return; + bool is_abstract = false; c = gfc_find_component (vtype, name, true, true, NULL); + /* If the present component typebound proc is abstract, the new version + should unconditionally be tested if it is a suitable replacement. */ + if (c && c->tb && c->tb->u.specific + && c->tb->u.specific->n.sym->attr.abstract) + is_abstract = true; + + /* Pass on the new tb being not overridable if a component is found and + either there is not an overridden specific or the present component + tb is abstract. This ensures that possible, viable replacements are + loaded. */ + if (tb->non_overridable && !tb->overridden && !is_abstract && c) + return; + if (c == NULL) { /* Add procedure component. */ diff --git a/gcc/testsuite/gfortran.dg/pr117730_a.f90 b/gcc/testsuite/gfortran.dg/pr117730_a.f90 new file mode 100644 index 000000000000..12e28214b02b --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr117730_a.f90 @@ -0,0 +1,50 @@ +! { dg-do compile } +! +! Test the fix for PR117730 in which the non_overrridable procedures in 'child' +! were mixied up in the vtable for the extension 'child2' in pr117730_b.f90. +! This resulted in 'this%calc()' in 'function child_get(this)' returning garbage +! when 'this' was of dynamic type 'child2'. +! +! Contributed by <dar...@web.de> in comment 4 of PR84674. +! +module module1 + implicit none + private + public :: child + + type, abstract :: parent + contains + procedure, pass :: reset => parent_reset + end type parent + + type, extends(parent), abstract :: child + contains + procedure, pass, non_overridable :: reset => child_reset + procedure, pass, non_overridable :: get => child_get + procedure(calc_i), pass, deferred :: calc + end type child + + abstract interface + pure function calc_i(this) result(value) + import :: child + class(child), intent(in) :: this + integer :: value + end function calc_i + end interface + +contains + pure subroutine parent_reset(this) + class(parent), intent(inout) :: this + end subroutine parent_reset + + pure subroutine child_reset(this) + class(child), intent(inout) :: this + end subroutine child_reset + + function child_get(this) result(value) + class(child), intent(inout) :: this + integer :: value + + value = this%calc() + end function child_get +end module module1 diff --git a/gcc/testsuite/gfortran.dg/pr117730_b.f90 b/gcc/testsuite/gfortran.dg/pr117730_b.f90 new file mode 100644 index 000000000000..09707882989e --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr117730_b.f90 @@ -0,0 +1,47 @@ +! { dg-do run } +! { dg-compile-aux-modules "pr117730_a.f90" } +! { dg-additional-sources pr117730_a.f90 } +! +! Test the fix for PR117730 in which the non_overrridable procedures in +! pr117730_a.f90 were mixied up in the vtable for 'child2' below. This resulted +! in 'this%calc()' in 'function child_get(this)' returning garbage. +! +! Contributed by <dar...@web.de> in comment 4 of PR84674. +! +module module2 + use module1, only: child + + implicit none + private + public :: child2 + + type, extends(child) :: child2 + contains + procedure, pass :: calc => child2_calc + end type child2 + +contains + + pure function child2_calc(this) result(value) + class(child2), intent(in) :: this + integer :: value + + value = 1 + end function child2_calc + +end module module2 + +program test + use module2, only: child2 + + implicit none + + type(child2) :: F + + if (F%calc() /= 1) stop 1 + + print *, "---------------" + if (F%get() /= 1) stop 2 + +end program test +! { dg-final { cleanup-modules "module1" } } diff --git a/gcc/testsuite/gfortran.dg/pr84674.f90 b/gcc/testsuite/gfortran.dg/pr84674.f90 new file mode 100644 index 000000000000..c58ae9efff69 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr84674.f90 @@ -0,0 +1,55 @@ +! { dg-do run } +! +! Test the fix for PR84674, in which the non-overridable variant of the +! procedure ff below caused a runtime segfault. +! +! Contributed by Jakub Benda <alban...@atlas.cz> +! + module m + implicit none + + type, abstract :: t1 + integer :: i + contains + procedure(i_f), pass(u), deferred :: ff + end type t1 + + type, abstract, extends(t1) :: t2 + contains + procedure, non_overridable, pass(u) :: ff => f ! Segmentation fault + !procedure, pass(u) :: ff => f ! worked + end type t2 + + type, extends(t2) :: DerivedType + end type DerivedType + + abstract interface + subroutine i_f(u) + import :: t1 + class(t1), intent(inout) :: u + end subroutine i_f + end interface + + contains + + subroutine f(u) + class(t2), intent(inout) :: u + u%i = 3*u%i + end subroutine f + + end module m + + + program p + + use m + + implicit none + + class(t1), allocatable :: v + + allocate(DerivedType::v) + v%i = 2 + call v%ff() + if (v%i /= 6) stop + end program p