------- Comment #9 from paul dot richard dot thomas at cea dot fr 2006-07-12
14:20 -------
Created an attachment (id=11867)
--> (http://gcc.gnu.org/bugzilla/attachment.cgi?id=11867&action=view)
For discussion, perusal and testing: a beta-release of the TR15581 patch
This patch represents some months of work by Erik and myself. It is still not
complete and has at least one residual source of memory leakage (derived type
constructors with function array-valued actuals). That withstanding, it does
most of the memory management required by the standard, it does assignments
correctly and handless allocatable components in contructors. There is still a
way to go before it is submittable but it's getting there!
What does it do?
(i) It runs most of the iso_varying_string testsuite (vst16.f95 fails in io,
vst28.f95, vst30.f95 and vst31.f95 need modification to catch zero length
strings).
(ii) This tests the basic functionality:
! { dg-do run}
! { dg-options "-O2 -fdump-tree-original" }
!
! Check some basic functionality of allocatable components, including that they
! are nullified when created and automatically deallocated when
! 1. A variable goes out of scope
! 2. INTENT(OUT) dummies
! 3. Function results
!
module alloc_m
implicit none
type :: alloc1
real, allocatable :: x(:)
end type alloc1
end module alloc_m
program alloc
use alloc_m
implicit none
type :: alloc2
type(alloc1), allocatable :: a1(:)
integer, allocatable :: a2(:)
end type alloc2
type(alloc2) :: b
integer :: i
type(alloc2), allocatable :: c(:)
if (allocated(b%a2) .OR. allocated(b%a1)) then
write (0, *) 'main - 1'
call abort()
end if
! 3 calls to _gfortran_deallocate (INTENT(OUT) dummy)
call allocate_alloc2(b)
call check_alloc2(b)
do i = 1, size(b%a1)
! 1 call to _gfortran_deallocate
deallocate(b%a1(i)%x)
end do
! 3 calls to _gfortran_deallocate (INTENT(OUT) dummy)
call allocate_alloc2(b)
call check_alloc2(return_alloc2())
! 3 calls to _gfortran_deallocate (function result)
allocate(c(1))
! 3 calls to _gfortran_deallocate (INTENT(OUT) dummy)
call allocate_alloc2(c(1))
! 4 calls to _gfortran_deallocate
deallocate(c)
! 7 calls to _gfortran_deallocate (b (3) and c(4) goes aout of scope)
contains
subroutine allocate_alloc2(b)
type(alloc2), intent(out) :: b
integer :: i
if (allocated(b%a2) .OR. allocated(b%a1)) then
write (0, *) 'allocate_alloc2 - 1'
call abort()
end if
allocate (b%a2(3))
b%a2 = [ 1, 2, 3 ]
allocate (b%a1(3))
do i = 1, 3
if (allocated(b%a1(i)%x)) then
write (0, *) 'allocate_alloc2 - 2', i
call abort()
end if
allocate (b%a1(i)%x(3))
b%a1(i)%x = i + [ 1.0, 2.0, 3.0 ]
end do
end subroutine allocate_alloc2
type(alloc2) function return_alloc2() result(b)
if (allocated(b%a2) .OR. allocated(b%a1)) then
write (0, *) 'return_alloc2 - 1'
call abort()
end if
allocate (b%a2(3))
b%a2 = [ 1, 2, 3 ]
allocate (b%a1(3))
do i = 1, 3
if (allocated(b%a1(i)%x)) then
write (0, *) 'return_alloc2 - 2', i
call abort()
end if
allocate (b%a1(i)%x(3))
b%a1(i)%x = i + [ 1.0, 2.0, 3.0 ]
end do
end function return_alloc2
subroutine check_alloc2(b)
type(alloc2), intent(in) :: b
if (.NOT.(allocated(b%a2) .AND. allocated(b%a1))) then
write (0, *) 'check_alloc2 - 1'
call abort()
end if
if (any(b%a2 /= [ 1, 2, 3 ])) then
write (0, *) 'check_alloc2 - 2'
call abort()
end if
do i = 1, 3
if (.NOT.allocated(b%a1(i)%x)) then
write (0, *) 'check_alloc2 - 3', i
call abort()
end if
if (any(b%a1(i)%x /= i + [ 1.0, 2.0, 3.0 ])) then
write (0, *) 'check_alloc2 - 4', i
call abort()
end if
end do
end subroutine check_alloc2
end program alloc
! { dg-final { scan-tree-dump-times "deallocate" 24 "original" } }
! { dg-final { cleanup-tree-dump "original" } }
(iii) The following tests constructors:
program
type :: mytype
integer, allocatable :: a(:, :)
end type mytype
type (mytype) :: x
integer :: y(0:1, -1:0) = reshape ((/42, 99, 55, 77/), (/2,2/))
x = mytype (y)
call foo (x, y)
x = mytype (reshape ((/42, 99, 55, 77/), (/2,2/)))
call foo (x, reshape ((/42, 99, 55, 77/), (/2,2/)))
x = mytype (bar (y))
call foo (x, y**3)
contains
subroutine foo (x, y)
type(mytype) :: x
integer y(:,:)
if (any (x%a .ne. y)) call abort ()
end subroutine foo
function bar (x)
integer, dimension(:,:) :: x
integer, dimension(size(x, 1), size(x, 2)) :: bar
bar = x**3
end function bar
end program
(iv) Whilst this tests assignments:
type :: ivs
character(1), allocatable :: chars(:)
end type ivs
type(ivs) :: a, b
type(ivs) :: x(3), y(3)
allocate(a%chars(5))
a%chars = (/"h","e","l","l","o"/)
! An intrinsic assignment must deallocate the l-value, copy across the
! array and null the descriptor data field of the r-value.
b = a
if (any (b%chars .ne. (/"h","e","l","l","o"/))) call abort ()
if (allocated (a%chars) .neqv. .false.) call abort ()
! Scalar to array needs to copy the derived type, to its ultimate components,
! to each of the l-value elements and then to deallocate the r-value. */
x = b
x(2)%chars = (/"g","'","d","a","y"/)
if (any (x(1)%chars .ne. (/"h","e","l","l","o"/))) call abort ()
if (any (x(2)%chars .ne. (/"g","'","d","a","y"/))) call abort ()
if (allocated (b%chars) .neqv. .false.) call abort ()
deallocate (x(1)%chars, x(2)%chars)
! Array intrinsic assignments are like their scalar counterpart and
! must deallocate each element of the l-value, copy across the
! arrays from the r-value elements and null the descriptor data field
! of the r-value elements.
allocate(x(1)%chars(5), x(2)%chars(5), x(3)%chars(5))
x(1)%chars = (/"h","e","l","l","o"/)
x(2)%chars = (/"g","'","d","a","y"/)
x(3)%chars = (/"g","o","d","a","g"/)
y(2:1:-1) = x(1:2)
if (allocated (x(1)%chars) .neqv. .false.) call abort ()
if (allocated (x(2)%chars) .neqv. .false.) call abort ()
if (allocated (x(3)%chars) .neqv. .true.) call abort ()
if (allocated (y(1)%chars) .neqv. .true.) call abort ()
if (allocated (y(2)%chars) .neqv. .true.) call abort ()
if (allocated (y(3)%chars) .neqv. .false.) call abort ()
if (any (y(1)%chars .ne. (/"g","'","d","a","y"/))) call abort ()
if (any (y(2)%chars .ne. (/"h","e","l","l","o"/))) call abort ()
if (any (x(3)%chars .ne. (/"g","o","d","a","g"/))) call abort ()
! In the case of an assignment where there is a dependency, so that a
! temporary is necessary, each element must be copied to its destination
! and the source element nullified.
y(2:3) = y(1:2)
if (allocated (y(1)%chars)) call abort ()
if (any (y(2)%chars .ne. (/"g","'","d","a","y"/))) call abort ()
if (any (y(3)%chars .ne. (/"h","e","l","l","o"/))) call abort ()
end
--
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=20541