------- 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

Reply via email to