使用类型构造函数分配的多态数组元素在访问时会导致分段错误
Polymorphic array element assigned using type constructor causes segmentation fault when accessed
在学习 Fortran 的一些面向对象特性的过程中,我试图制作一个用户定义类型 (wrapper
) 的数组 (group
) 和一个多态组件 (obj
).多态组件是class(parent)
,我想将它分配给例如type(child)
,其中 child
扩展类型 parent
。
如果我为child
使用类型构造函数来分配数组元素group(1)%obj = child(1.)
,分配似乎成功了,但是,当访问组件时例如group(1)%obj%val
,当 运行 可执行文件时发生段错误。只有当多态组件是数组元素时才会发生这种情况。如果我使用可分配标量 obj
,分配和后续访问将按预期进行。此外,在数组的情况下,如果我宁愿使用源分配,或者将分配从标量移动到数组元素,我又会得到预期的行为。
所描述的行为是使用 gfortran (9.2.0) 编译时观察到的。使用 ifort (19) 或 nagfor (6.1),代码可以按预期编译和运行。据我了解,&'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,有许多与多态性相关的问题,但我找不到完全符合我的具体问题的问题。
因此,我的问题是:
- 下面显示的代码是否有效 Fortran 以及由于 gfortran 中的错误而观察到的行为?
- 或者,如果我写了无效的 Fortran 有错(幸运的是没有用 ifort 和 nagfor 引起 WW3),我的错误在哪里?
这是一个 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
让我们简化您的代码;在决定是否存在真正需要解决的编译器错误时。下面的代码为我提供了 gfortran 8 和 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
这个程序是有效的 Fortran 程序吗?是的
将程序缩小一点可以告诉我们关于完整案例的什么信息?在最小情况下,这里的问题似乎出在多态变量 (group(1)%obj = parent(1.)
) 的固有赋值中——这是现代 Fortran 代码中一个值得注意的问题区域。如果我们用源分配替换这个内部赋值,就没有运行时失败,预期结果:
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
问题示例中的源分配也避免了运行时问题。我们可以在没有源分配的情况下看到大致相同的情况,但对组件进行分配和内部分配或默认初始化。
结论:是的,这是一个 gfortran 错误(/缺乏对正确内部赋值的支持)。
在学习 Fortran 的一些面向对象特性的过程中,我试图制作一个用户定义类型 (wrapper
) 的数组 (group
) 和一个多态组件 (obj
).多态组件是class(parent)
,我想将它分配给例如type(child)
,其中 child
扩展类型 parent
。
如果我为child
使用类型构造函数来分配数组元素group(1)%obj = child(1.)
,分配似乎成功了,但是,当访问组件时例如group(1)%obj%val
,当 运行 可执行文件时发生段错误。只有当多态组件是数组元素时才会发生这种情况。如果我使用可分配标量 obj
,分配和后续访问将按预期进行。此外,在数组的情况下,如果我宁愿使用源分配,或者将分配从标量移动到数组元素,我又会得到预期的行为。
所描述的行为是使用 gfortran (9.2.0) 编译时观察到的。使用 ifort (19) 或 nagfor (6.1),代码可以按预期编译和运行。据我了解,
因此,我的问题是:
- 下面显示的代码是否有效 Fortran 以及由于 gfortran 中的错误而观察到的行为?
- 或者,如果我写了无效的 Fortran 有错(幸运的是没有用 ifort 和 nagfor 引起 WW3),我的错误在哪里?
这是一个 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
让我们简化您的代码;在决定是否存在真正需要解决的编译器错误时。下面的代码为我提供了 gfortran 8 和 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
这个程序是有效的 Fortran 程序吗?是的
将程序缩小一点可以告诉我们关于完整案例的什么信息?在最小情况下,这里的问题似乎出在多态变量 (group(1)%obj = parent(1.)
) 的固有赋值中——这是现代 Fortran 代码中一个值得注意的问题区域。如果我们用源分配替换这个内部赋值,就没有运行时失败,预期结果:
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
问题示例中的源分配也避免了运行时问题。我们可以在没有源分配的情况下看到大致相同的情况,但对组件进行分配和内部分配或默认初始化。
结论:是的,这是一个 gfortran 错误(/缺乏对正确内部赋值的支持)。