Strange result with polymorphic variables
Janus Weil
janus@gcc.gnu.org
Mon Aug 29 11:40:00 GMT 2011
Hi Arjen,
> the program below produces the following unexpected result:
well, for me the program you posted directly produces a segfault with
all gfortran versions I tried (4.5-4.7):
jweil@manzanita:~/fort> gfortran-4.7 -fcheck=all -Wall -g3 -O0 random_walk.f90
jweil@manzanita:~/fort> ./a.out
Two-dimensional walk:
Backtrace for this error:
#0 /usr/lib64/libgfortran.so.3(+0x1ab67)[0x7f75c305fb67]
#1 /usr/lib64/libgfortran.so.3(+0x1b52f)[0x7f75c306052f]
#2 /lib64/libc.so.6(+0x32b30)[0x7f75c2667b30]
#3 /lib64/libc.so.6(cfree+0x19)[0x7f75c26ad289]
#4 /home/jweil/fort/a.out[0x400C00] in
__points2d3d_MOD_scale_by_factor_2d at random_walk.f90:139
#5 /home/jweil/fort/a.out[0x401835] in random_walk at random_walk.f90:207
Line 139 is the DEALLOCATE statement in scale_by_factor_2d. However,
since this is guarded by an ALLOCATED(...), it does not really look
like a user error, but more like a compiler bug. I think it would be
helpful to reduce the program a bit, in order to nail down the error.
Cheers,
Janus
> ------------
> ! random_walk.f90 --
> ! Simulate a random walk in two and three dimensions
> !
> module points2d3d
>
> implicit none
>
> type point2d
> real :: x, y
> contains
> procedure :: print => print_2d
> procedure :: random_vector => random_vector_2d
> procedure :: add_vector => add_vector_2d
> procedure, pass(vector) :: scale_by_factor => scale_by_factor_2d
> procedure :: assign => assign_2d
> generic :: operator(+) => add_vector
> generic :: operator(*) => scale_by_factor
> generic :: assignment(=) => assign
> end type point2d
>
> type, extends(point2d) :: point3d
> real :: z
> contains
> procedure :: print => print_3d
> procedure :: random_vector => random_vector_3d
> procedure :: add_vector => add_vector_3d
> procedure, pass(vector) :: scale_by_factor => scale_by_factor_3d
> procedure :: assign => assign_3d
> end type point3d
>
> contains
>
> !
> ! Auxiliary routine to convert from class to point
> !
> subroutine assign_2d( point1, point2 )
> class(point2d), intent(inout) :: point1
> class(point2d), intent(in) :: point2
>
> point1%x = point2%x
> point1%y = point2%y
>
> end subroutine assign_2d
>
> subroutine assign_3d( point1, point2 )
> class(point3d), intent(inout) :: point1
> class(point2d), intent(in) :: point2
>
> point1%x = point2%x
> point1%y = point2%y
> point1%z = 0.0
>
> select type (point2)
> class is (point3d)
> point1%z = point2%z
> end select
>
> end subroutine assign_3d
>
> subroutine print_2d( point )
> class(point2d) :: point
>
> write(*,'(2f10.4)') point%x, point%y
> end subroutine print_2d
>
> subroutine print_3d( point )
> class(point3d) :: point
>
> write(*,'(3f10.4)') point%x, point%y, point%z
> end subroutine print_3d
>
> subroutine random_vector_2d( point )
> class(point2d) :: point
>
> call random_number( point%x )
> call random_number( point%y )
>
> point%x = 2.0 * (point%x - 0.5)
> point%y = 2.0 * (point%y - 0.5)
>
> end subroutine random_vector_2d
>
> subroutine random_vector_3d( point )
> class(point3d) :: point
>
> call point%point2d%random_vector
> call random_number( point%z )
>
> point%z = 2.0 * (point%z - 0.5)
>
> end subroutine random_vector_3d
>
> function add_vector_2d( point, vector )
> class(point2d), intent(in) :: point, vector
> class(point2d), allocatable :: add_vector_2d
>
> ! Workaround for gfortran 4.6
> if ( allocated( add_vector_2d ) ) then
> deallocate( add_vector_2d )
> endif
>
> allocate( add_vector_2d )
> add_vector_2d%x = point%x + vector%x
> add_vector_2d%y = point%y + vector%y
>
> end function add_vector_2d
>
> function add_vector_3d( point, vector )
> class(point3d), intent(in) :: point
> class(point2d), intent(in) :: vector
>
> class(point2d), allocatable :: add_vector_3d
> class(point3d), allocatable :: add_result
>
> allocate( add_result )
>
> select type (vector)
> class is (point3d)
> !add_result%point2d = point%point2d + vector%point2d
> add_result%point2d = point%point2d%add_vector( vector%point2d )
> add_result%z = point%z + vector%z
>
> class default
> add_result%point2d = point%point2d + vector
> add_result%z = 0.0
> end select
>
> call move_alloc( add_result, add_vector_3d )
>
> end function add_vector_3d
>
> function scale_by_factor_2d( factor, vector )
> real, intent(in) :: factor
> class(point2d), intent(in) :: vector
> class(point2d), allocatable :: scale_by_factor_2d
>
> ! Workaround for gfortran 4.6
> if ( allocated( scale_by_factor_2d ) ) then
> deallocate( scale_by_factor_2d )
> endif
>
> allocate( scale_by_factor_2d )
>
> scale_by_factor_2d%x = factor * vector%x
> scale_by_factor_2d%y = factor * vector%y
>
> end function scale_by_factor_2d
>
> function scale_by_factor_3d( factor, vector )
> real, intent(in) :: factor
> class(point3d), intent(in) :: vector
>
> class(point2d), allocatable :: scale_by_factor_3d
> class(point3d), allocatable :: scale_result
>
> allocate( scale_result )
>
> scale_result%point2d = factor * vector%point2d
> scale_result%z = factor * vector%z
>
> call move_alloc( scale_result, scale_by_factor_3d )
>
> end function scale_by_factor_3d
>
> end module points2d3d
>
> program random_walk
>
> use points2d3d ! Both 2D and 3D points available
>
> type(point2d), target :: point_2d
> type(point3d), target :: point_3d
> type(point2d), target :: vector_2d
> type(point3d), target :: vector_3d
>
> !
> ! A variable of class point2d can point to point_2d but
> ! also to point_3d
> !
> class(point2d), pointer :: point
> class(point2d), pointer :: vector
>
> integer :: nsteps = 10
> integer :: i
> integer :: trial
> real :: deltt = 0.1
>
> do trial = 1,2
>
> ! Select what type of point ...
>
> if ( trial == 1 ) then
> point => point_2d
> vector => vector_2d
> write(*,*) 'Two-dimensional walk:'
> else
> point => point_3d
> vector => vector_3d
> write(*,*) 'Three-dimensional walk:'
> endif
>
> call point%random_vector
>
> do i = 1,nsteps
> call vector%random_vector
>
> point = point + deltt * vector
>
> call point%print
> enddo
> enddo
> end program random_walk
>
More information about the Fortran
mailing list