2

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

1 Answer 1

3

Let's simplify your code; when deciding whether there's a compiler bug that really is the way to go. The code below gives a segmentation fault for me with gfortran 8 and 10.

program poly_array

  type :: parent
    real :: val
  end type parent

  type :: wrapper
    class(parent), allocatable :: obj
  end type wrapper

  type(wrapper), allocatable :: group(:)

  allocate(group(1))
  group(1)%obj = parent(1.)
  write(*, *) group(1)%obj%val

end program poly_array

Is this program a valid Fortran program? Yes.

What can reducing the program down a little tell us about the full case? The problem here in the minimal case appears to be in the intrinsic assignment of a polymorphic variable (group(1)%obj = parent(1.)) - a notable problematic area in modern Fortran code. If we replace this intrinsic assignment with sourced allocation, there's no runtime failure, and the expected result:

program poly_array

  type :: parent
    real :: val
  end type parent

  type :: wrapper
    class(parent), allocatable :: obj
  end type wrapper

  type(wrapper), allocatable :: group(:)

  allocate(group(1))
  allocate(group(1)%obj, source=parent(1.))
  write(*, *) group(1)%obj%val

end program poly_array

The sourced allocation in the example of the question also avoids the runtime problem. We can see much the same without sourced allocation but with allocation and intrinsic assignment to the component or default initialization.

Conclusion: yes, it's a gfortran bug(/lack of support for correct intrinsic assignment).

Sign up to request clarification or add additional context in comments.

1 Comment

Confirmation that this is indeed a problematic case of intrinsic assignment to an allocatable polymorphic variable, made me take another look at reported gfortran bugs. I believe that this is bug 81411, and if so, your code from this answer is a significantly more concise reproducer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.