In the process of learning some object oriented features of Fortran, I am attempting to make an array (group) of a user defined type (wrapper) with a polymorphic component (obj). The polymorphic component is of class(parent), and I want to allocate it to e.g. type(child), where child extends type parent.
If I use a type constructor for child to allocate an array element group(1)%obj = child(1.), the allocation seems to succeed, however, when accessing the component e.g. group(1)%obj%val, a segmentation fault occurs when running the executable. This only happens if the polymorphic component is an array element. If I use an allocatable scalar obj, allocation and subsequent access works as expected. Furthermore, in the array case, if I rather use sourced allocation, or move allocation from a scalar to an array element, I again get the expected behaviour.
The described behaviour is observed using gfortran (9.2.0) to compile. Using either ifort (19) or nagfor (6.1) the code compiles and runs as expected. To my understanding, Q&A's on this site and others indicate that what I am trying to do is in principle valid. Looking at a list of gfortran bugs, there are a number of issues related to polymorphism, but I cannot quite find one exactly matching my specific problem.
Thus, my question is this:
- Is the code presented below valid Fortran and the observed behaviour due to a bug in gfortran?
- Or, if I am at fault for writing invalid Fortran (and just fortunate not to have caused WW3 with ifort and nagfor), where is my error?
Here is a MCVE fully illustrating what I am attempting to do (could be made more minimal if only to reproduce the error):
module udt_m
implicit none
type, abstract :: parent
real :: val
end type parent
type, extends(parent) :: child
end type child
interface child
procedure child_constructor
end interface
contains
function child_constructor(val) result(out)
implicit none
real, intent(in) :: val
type(child) :: out
out%val = val
end function child_constructor
end module udt_m
program poly_array
use udt_m
implicit none
class(parent), allocatable :: obj
type :: wrapper
class(parent), allocatable :: obj
end type wrapper
type(wrapper), allocatable :: group(:)
! scalar instance
obj = child(1.)
if (allocated(obj)) then
write(*, '(g0)') 'obj allocated'
write(*, '(*(g0))') 'obj%val=', obj%val
end if
! array wrapped instance
allocate(group(1))
group(1)%obj = child(1.) ! constructor assignment seemingly works, later access fails with gfortran
! group(1)%obj = obj ! workaround: scalar temporary
! allocate(group(1)%obj, source=child(1.)) ! workaround: sourced allocation
! call move_alloc(from=obj, to=group(1)%obj) ! Workaround: call move_alloc(from=scalar, to=array element)
if (allocated(group(1)%obj)) then
write(*, '(g0)') 'group(1)%obj allocated'
write(*, '(*(g0))') 'group(1)%obj%val=', group(1)%obj%val ! access causes segmentation fault with gfortran
end if
end program poly_array
Compiled using:
gfortran -Og -g -fbacktrace -Wall -Wextra -Wpedantic -fcheck=all -std=f2008 -fsanitize=address,undefined -o poly_array.out poly_array.f90
Actual output (obtained with gfortran)
./poly_array.out
obj allocated
obj%val=1.00000000
group(1)%obj allocated
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
...
Expected output (obtained with ifort or nagfor):
./poly_array.out
obj allocated
obj%val=1.000000
group(1)%obj allocated
group(1)%obj%val=1.000000