! { dg-do run }
!
! class based quick sort program - starting point comment #0 of pr41539
!
! Note assignment with vector index reference fails because temporary
! allocation does not occur - also false dependency detected. Nullification
! of temp descriptor data causes a segfault.
!
module m_qsort
 implicit none
 type, abstract :: sort_t
 contains
   procedure(disp), deferred :: disp
   procedure(lt_cmp), deferred :: lt_cmp
   procedure(assign), deferred :: assign
   generic :: operator(<) => lt_cmp
   generic :: assignment(=) => assign
 end type sort_t
 interface
   elemental integer function disp(a)
     import
     class(sort_t), intent(in) :: a
   end function disp
 end interface
 interface
   impure elemental logical function lt_cmp(a,b)
     import
     class(sort_t), intent(in) :: a, b
   end function lt_cmp
 end interface
 interface
   elemental subroutine assign(a,b)
     import
     class(sort_t), intent(out) :: a
     class(sort_t), intent(in) :: b
   end subroutine assign
 end interface
contains

 subroutine qsort(a)
   class(sort_t), intent(inout),allocatable :: a(:)
   class(sort_t), allocatable :: tmp (:)
   integer, allocatable :: index_array (:)
   integer :: i
   allocate (tmp(size (a, 1)), source = a)
   index_array = [(i, i = 1, size (a, 1))]
   call internal_qsort (tmp, index_array)   ! Do not move class elements around until end
   a = tmp(index_array)
 end subroutine qsort

 recursive subroutine internal_qsort (x, iarray)
   class(sort_t), intent(inout),allocatable :: x(:)
   class(sort_t), allocatable :: ptr
   integer, allocatable :: iarray(:), above(:), below(:), itmp(:)
   integer :: pivot, nelem, i, iptr
   if (.not.allocated (iarray)) return
   nelem = size (iarray, 1)
   if (nelem .le. 1) return
   pivot = nelem / 2
   allocate (ptr, source = x(iarray(pivot))) ! Pointer to the pivot element
   do i = 1, nelem
     iptr = iarray(i)                  ! Index for i'th element
     if (ptr%lt_cmp (x(iptr))) then    ! Compare pivot with i'th element
       itmp = [iptr]
       above = concat (itmp, above)    ! Invert order to prevent infinite loops
     else
       itmp = [iptr]
       below = concat (itmp, below)    ! -ditto-
     end if
   end do
   call internal_qsort (x, above)      ! Recursive sort of 'above' and 'below'
   call internal_qsort (x, below)
   iarray = concat (below, above)      ! Concatenate the result
 end subroutine internal_qsort

 function concat (ia, ib) result (ic)
   integer, allocatable, dimension(:) :: ia, ib, ic
   if (allocated (ia) .and. allocated (ib)) then
     ic = [ia, ib]
   else if (allocated (ia)) then
     ic = ia
   else if (allocated (ib)) then
     ic = ib
   end if
 end function concat
end module m_qsort

module test
 use m_qsort
 implicit none
 type, extends(sort_t) :: sort_int_t
   integer :: i
 contains
   procedure :: disp => disp_int
   procedure :: lt_cmp => lt_cmp_int
   procedure :: assign => assign_int
 end type
contains
 elemental integer function disp_int(a)
     class(sort_int_t), intent(in) :: a
     disp_int = a%i
 end function disp_int
 elemental subroutine assign_int (a, b)
   class(sort_int_t), intent(out) :: a
   class(sort_t), intent(in) :: b         ! TODO: gfortran does not throw 'class(sort_int_t)'
   select type (b)
     class is (sort_int_t)
       a%i = b%i
     class default
       a%i = -1
   end select
 end subroutine assign_int
 impure elemental logical function lt_cmp_int(a,b) result(cmp)
   class(sort_int_t), intent(in) :: a
   class(sort_t), intent(in) :: b
   select type(b)
     type is(sort_int_t)
       if (a%i < b%i) then
         cmp = .true.
       else
         cmp = .false.
       end if
     class default
       ERROR STOP "Don't compare apples with oranges"
   end select
 end function lt_cmp_int
end module test

program main
 use test
 class(sort_t), allocatable :: A(:)
 integer :: i, m(5)= [7 , 4, 5, 2, 3]
 allocate (A(5), source = [(sort_int_t(m(i)), i=1,5)])
!  print *, "Before qsort: ", A%disp()
 call qsort(A)
!  print *, "After qsort:  ", A%disp()
 if (any (A%disp() .ne. [2,3,4,5,7])) call abort
end program main
