В процессе изучения некоторых объектно-ориентированных функций Fortran я пытаюсь создать массив (group
) определенного пользователем типа (wrapper
) с компонентом polymorphi c (obj
). Компонент polymorphi c имеет class(parent)
, и я хочу выделить его, например, type(child)
, где child
расширяет тип parent
.
Если я использую конструктор типа для child
для выделения элемента массива group(1)%obj = child(1.)
, выделение кажется успешным, однако при доступе к компоненту, например, group(1)%obj%val
, при запуске исполняемого файла возникает ошибка сегментации. Это происходит только в том случае, если компонент polymorphi c является элементом массива. Если я использую распределяемый скаляр obj
, распределение и последующий доступ будут работать должным образом. Кроме того, в случае с массивом, если я предпочитаю использовать выделение из источника или перемещать выделение из скаляра в элемент массива, я снова получаю ожидаемое поведение.
Описанное поведение наблюдается при использовании gfortran (9.2.0) компилировать. Используя ifort (19) или nagfor (6.1), код компилируется и работает, как ожидалось. Насколько я понимаю, Q & A на этом сайте и другие указывают на то, что то, что я пытаюсь сделать, в принципе верно. Глядя на список ошибок gfortran , можно увидеть ряд проблем, связанных с полиморфизмом, но я не могу найти ни одной, точно соответствующей моей конкретной проблеме c.
Таким образом, мой вопрос: это:
- Является ли представленный ниже код допустимым Fortran и наблюдаемое поведение из-за ошибки в gfortran?
- Или, если я виноват в написании недопустимого Fortran (и мне просто повезло чтобы не вызвать WW3 с ifort и nagfor), где моя ошибка?
Вот MCVE, полностью иллюстрирующий то, что я пытаюсь сделать (можно было бы сделать более минимальным, если бы только воспроизвести ошибку) :
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
Скомпилировано с использованием:
gfortran -Og -g -fbacktrace -Wall -Wextra -Wpedantic -fcheck=all -std=f2008 -fsanitize=address,undefined -o poly_array.out poly_array.f90
Фактический результат (полученный с помощью gfortran)
./poly_array.out
obj allocated
obj%val=1.00000000
group(1)%obj allocated
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
...
Ожидаемый результат (полученный с помощью ifort или nagfor):
./poly_array.out
obj allocated
obj%val=1.000000
group(1)%obj allocated
group(1)%obj%val=1.000000