Bug 84674 - [11/12/13/14 Regression] Derived type name change makes a program segfault, removing non_overridable helps
Summary: [11/12/13/14 Regression] Derived type name change makes a program segfault, r...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 8.0.1
: P4 normal
Target Milestone: 11.5
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2018-03-02 15:14 UTC by Jakub Benda
Modified: 2023-07-07 10:33 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work: 6.4.0
Known to fail: 7.3.0, 8.0, 9.0
Last reconfirmed: 2018-03-02 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jakub Benda 2018-03-02 15:14:01 UTC
Take the program code from https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61284#c0 , change derived type name "t3" to "DerivedType" (three places in the code) and compile the result by gfortran 7.3.0. It will compile fine, but result in segmentation fault at runtime, even though the original code (with "t3") now runs all right.

When the modified code is compiled with gfortran 7.2.0 or ifort 17.0.0, it also runs well, without crashing.

Finally, when "non_overridable" keyword on line 12 is removed (as indicated in the commented line 13), the modified program runs well, too, even when compiled by gfortran 7.3.0.

For reference, here is the failing code:

 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 ! works
  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()
  write(*,*) v%i
 end program p
Comment 1 Thomas Koenig 2018-03-02 19:08:00 UTC
This has to be one of the stranger bugs I have encountered...

It appears that the bug depends on the alphabetical ordering
of the type names. So, replace DerivedType in your code
by anything (such as "t2a") that comes later in the alphabet
than t2, the code compiles and runs fine.

Now, replacing DerivedType with t0 and t3, respectively, and
comparing the assembly dumps, shows some significant difference.

The t3 version has, right at the top

	.file	"a.f90"
	.text
	.globl	__m_MOD___def_init_m_T3
	.section	.rodata
	.align 4
	.type	__m_MOD___def_init_m_T3, @object
	.size	__m_MOD___def_init_m_T3, 4
__m_MOD___def_init_m_T3:
	.zero	4
	.globl	__m_MOD___vtab_m_T1
	.align 32
	.type	__m_MOD___vtab_m_T1, @object
	.size	__m_MOD___vtab_m_T1, 64

and the t0 version (which does not work) has

	.file	"a.f90"
	.text
	.globl	__m_MOD___def_init_m_T0
	.section	.rodata
	.align 4
	.type	__m_MOD___def_init_m_T0, @object
	.size	__m_MOD___def_init_m_T0, 4
__m_MOD___def_init_m_T0:
	.zero	4
	.globl	__m_MOD___vtab_m_T0
	.align 32
	.type	__m_MOD___vtab_m_T0, @object
	.size	__m_MOD___vtab_m_T0, 64

so whatever the def_init_m data structures are supposed to point to,
they appear to point to something wrong if things are out of
alphabetical order.

Wow, this has to be among the top 2% in weird gfortran bugs.
Comment 2 Dominique d'Humieres 2018-03-02 22:01:13 UTC
Regression! We are looking for a commit between r254227 (2017-10-30, OK) and r254498 (2017-11-07, wrong-code) that has been back-ported to gcc7, may be r254244(?).
Comment 3 Jakub Benda 2018-07-29 19:37:18 UTC
Bisection points to revision 254427 from 5 November 2017, which adds this chunk of code to "fortran/resolv.c" (function "resolve_fl_derived", lines 14081-14093):

  /* Generate module vtables subject to their accessibility and their not
     being vtables or pdt templates. If this is not done class declarations
     in external procedures wind up with their own version and so SELECT TYPE
     fails because the vptrs do not have the same address.  */
  if (gfc_option.allow_std & GFC_STD_F2003
      && sym->ns->proc_name
      && sym->ns->proc_name->attr.flavor == FL_MODULE
      && sym->attr.access != ACCESS_PRIVATE
      && !(sym->attr.use_assoc || sym->attr.vtype || sym->attr.pdt_template))
    {
      gfc_symbol *vtab = gfc_find_derived_vtab (sym);
      gfc_set_sym_referenced (vtab);
    }

When I comment it out, the compiled program works as expected.
Comment 4 Libavius 2018-09-25 13:19:28 UTC
I got a bug, which I think is related to the one described here and wanted to avoid opening another bug report.

I tried my best to build a small minimal example (see bottom). It compiles with all compilers I tried. However with gfortran 6.3.1, gfortran 7.3.1 and gfortran 8.1.0 it does not produce the wanted result (see test.f90).
With gfortran 4.8.5, nag 6.2 and intel 18.0.3 it runs as expected.
I carried out all tests on CentOS 7.4 and Ubuntu 16.04.

With a debugger, I could see: in test.f90, F%get() is called (line 8), the program then moves into child_get() in module2.f90 (line 35) as it should. However, stepping into this%calc() (line 39), it moves to child_reset() in module1.f90 (line 31) instead of child2_calc() in module2.f90 (line 15).

There are two ways to get this program to work, which are both quite weird:
1. remove "non_overridable" from line 14 in module1.f90
old: procedure, pass, non_overridable :: get => child_get
new: procedure, pass :: get => child_get

2. merge module1 and module2 into one module.


FILES:
***module1.f90:
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

***module2.f90:
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

***test.f90:
program test
    use module2, only: child2

    implicit none

    type(child2) :: F

    if (F%get() /= 1) stop ': FAILED'

end program test
Comment 5 Libavius 2018-09-25 13:23:58 UTC
(In reply to Libavius from comment #4)

> There are two ways to get this program to work, which are both quite weird:
> 1. remove "non_overridable" from line 14 in module1.f90
> old: procedure, pass, non_overridable :: get => child_get
> new: procedure, pass :: get => child_get

Sorry for this, I meant line 13:
old: procedure, pass, non_overridable :: reset => child_reset
new: procedure, pass :: reset => child_reset
Comment 6 Richard Biener 2019-11-14 07:58:42 UTC
The GCC 7 branch is being closed, re-targeting to GCC 8.4.
Comment 7 Jakub Jelinek 2020-03-04 09:52:08 UTC
GCC 8.4.0 has been released, adjusting target milestone.
Comment 8 Jakub Jelinek 2021-05-14 09:50:03 UTC
GCC 8 branch is being closed.
Comment 9 Richard Biener 2021-06-01 08:10:29 UTC
GCC 9.4 is being released, retargeting bugs to GCC 9.5.
Comment 10 Richard Biener 2022-05-27 09:38:32 UTC
GCC 9 branch is being closed
Comment 11 Jakub Jelinek 2022-06-28 10:34:43 UTC
GCC 10.4 is being released, retargeting bugs to GCC 10.5.
Comment 12 Richard Biener 2023-07-07 10:33:25 UTC
GCC 10 branch is being closed.