! RUN: %python %S/test_errors.py %s %flang_fc1
! C1140 -- A statement that might result in the deallocation of a polymorphic 
! entity shall not appear within a DO CONCURRENT construct.
module m1
  ! Base type with scalar components
  type :: Base
    integer :: baseField1
  end type

  ! Child type so we can allocate polymorphic entities
  type, extends(Base) :: ChildType
    integer :: childField
  end type

  ! Type with a polymorphic, allocatable component
  type, extends(Base) :: HasAllocPolyType
    class(Base), allocatable :: allocPolyField
  end type

  ! Type with a allocatable, coarray component
  type :: HasAllocCoarrayType
    type(Base), allocatable, codimension[:] :: allocCoarrayField
  end type

  ! Type with a polymorphic, allocatable, coarray component
  type :: HasAllocPolyCoarrayType
    class(Base), allocatable, codimension[:] :: allocPolyCoarrayField
  end type

  ! Type with a polymorphic, pointer component
  type, extends(Base) :: HasPointerPolyType
    class(Base), pointer :: pointerPolyField
  end type

  class(Base), allocatable :: baseVar1
  type(Base) :: baseVar2
end module m1

subroutine s1()
  ! Test deallocation of polymorphic entities caused by block exit
  use m1

  block
    ! The following should not cause problems
    integer :: outerInt

    ! The following are OK since they're not in a DO CONCURRENT
    class(Base), allocatable :: outerAllocatablePolyVar
    class(Base), allocatable, codimension[:] :: outerAllocatablePolyCoarray
    type(HasAllocPolyType), allocatable  :: outerAllocatableWithAllocPoly
    type(HasAllocPolyCoarrayType), allocatable :: outerAllocWithAllocPolyCoarray

    do concurrent (i = 1:10)
      ! The following should not cause problems
      block
        integer, allocatable :: blockInt
      end block
      block
        ! Test polymorphic entities
        ! OK because it's a pointer to a polymorphic entity
        class(Base), pointer :: pointerPoly

        ! OK because it's not polymorphic
        integer, allocatable :: intAllocatable

        ! OK because it's not polymorphic
        type(Base), allocatable :: allocatableNonPolyBlockVar

        ! Bad because it's polymorphic and allocatable
        class(Base), allocatable :: allocatablePoly

        ! OK because it has the SAVE attribute
        class(Base), allocatable, save :: allocatablePolySave

        ! Bad because it's polymorphic and allocatable
        class(Base), allocatable, codimension[:] :: allocatablePolyCoarray

        ! OK because it's not polymorphic and allocatable
        type(Base), allocatable, codimension[:] :: allocatableCoarray

        ! Bad because it has a allocatable polymorphic component
        type(HasAllocPolyType), allocatable  :: allocatableWithAllocPoly

        ! OK because the declared variable is not allocatable
        type(HasAllocPolyType) :: nonAllocatableWithAllocPoly

        ! OK because the declared variable is not allocatable
        type(HasAllocPolyCoarrayType) :: nonAllocatableWithAllocPolyCoarray

        ! Bad because even though the declared the allocatable component is a coarray
        type(HasAllocPolyCoarrayType), allocatable :: allocWithAllocPolyCoarray

        ! OK since it has no polymorphic component
        type(HasAllocCoarrayType) :: nonAllocWithAllocCoarray

        ! OK since it has no component that's polymorphic, oops
        type(HasPointerPolyType), allocatable :: allocatableWithPointerPoly

!ERROR: Deallocation of a polymorphic entity caused by block exit not allowed in DO CONCURRENT
!ERROR: Deallocation of a polymorphic entity caused by block exit not allowed in DO CONCURRENT
!ERROR: Deallocation of a polymorphic entity caused by block exit not allowed in DO CONCURRENT
!ERROR: Deallocation of a polymorphic entity caused by block exit not allowed in DO CONCURRENT
      end block
    end do
  end block

end subroutine s1

subroutine s2()
  ! Test deallocation of a polymorphic entity cause by intrinsic assignment
  use m1

  class(Base), allocatable :: localVar
  class(Base), allocatable :: localVar1
  type(Base), allocatable :: localVar2

  type(HasAllocPolyType), allocatable :: polyComponentVar
  type(HasAllocPolyType), allocatable :: polyComponentVar1

  type(HasAllocPolyType) :: nonAllocPolyComponentVar
  type(HasAllocPolyType) :: nonAllocPolyComponentVar1
  class(HasAllocPolyCoarrayType), allocatable :: allocPolyCoarray
  class(HasAllocPolyCoarrayType), allocatable :: allocPolyCoarray1

  class(Base), allocatable, codimension[:] :: allocPolyComponentVar
  class(Base), allocatable, codimension[:] :: allocPolyComponentVar1

  allocate(ChildType :: localVar)
  allocate(ChildType :: localVar1)
  allocate(Base :: localVar2)
  allocate(polyComponentVar)
  allocate(polyComponentVar1)
  allocate(allocPolyCoarray)
  allocate(allocPolyCoarray1)

  ! These are OK because they're not in a DO CONCURRENT
  localVar = localVar1
  nonAllocPolyComponentVar = nonAllocPolyComponentVar1
  polyComponentVar = polyComponentVar1
  allocPolyCoarray = allocPolyCoarray1

  do concurrent (i = 1:10)
    ! Test polymorphic entities
    ! Bad because localVar is allocatable and polymorphic, 10.2.1.3, par. 3
!ERROR: Deallocation of a polymorphic entity caused by assignment not allowed in DO CONCURRENT
    localVar = localVar1

    ! The next one should be OK since localVar2 is not polymorphic
    localVar2 = localVar1

    ! Bad because the copying of the components causes deallocation
!ERROR: Deallocation of a polymorphic entity caused by assignment not allowed in DO CONCURRENT
    nonAllocPolyComponentVar = nonAllocPolyComponentVar1

    ! Bad because possible deallocation a variable with a polymorphic component
!ERROR: Deallocation of a polymorphic entity caused by assignment not allowed in DO CONCURRENT
    polyComponentVar = polyComponentVar1

    ! Bad because deallocation upon assignment happens with allocatable
    ! entities, even if they're coarrays.  The noncoarray restriction only
    ! applies to components
!ERROR: Deallocation of a polymorphic entity caused by assignment not allowed in DO CONCURRENT
    allocPolyCoarray = allocPolyCoarray1

  end do
end subroutine s2

subroutine s3()
  ! Test direct deallocation
  use m1

  class(Base), allocatable :: polyVar
  type(Base), allocatable :: nonPolyVar
  type(HasAllocPolyType), allocatable :: polyComponentVar
  type(HasAllocPolyType), pointer :: pointerPolyComponentVar

  allocate(ChildType:: polyVar)
  allocate(nonPolyVar)
  allocate(polyComponentVar)
  allocate(pointerPolyComponentVar)

  ! These are all good because they're not in a do concurrent
  deallocate(polyVar)
  allocate(polyVar)
  deallocate(polyComponentVar)
  allocate(polyComponentVar)
  deallocate(pointerPolyComponentVar)
  allocate(pointerPolyComponentVar)

  do concurrent (i = 1:10)
    ! Bad because deallocation of a polymorphic entity
!ERROR: Deallocation of a polymorphic entity caused by a DEALLOCATE statement not allowed in DO CONCURRENT
    deallocate(polyVar)

    ! Bad, deallocation of an entity with a polymorphic component
!ERROR: Deallocation of a polymorphic entity caused by a DEALLOCATE statement not allowed in DO CONCURRENT
    deallocate(polyComponentVar)

    ! Bad, deallocation of a pointer to an entity with a polymorphic component
!ERROR: Deallocation of a polymorphic entity caused by a DEALLOCATE statement not allowed in DO CONCURRENT
    deallocate(pointerPolyComponentVar)

    ! Deallocation of a nonpolymorphic entity
    deallocate(nonPolyVar)
  end do
end subroutine s3

module m2
  type :: impureFinal
   contains
    final :: impureSub
  end type

  type :: pureFinal
   contains
    final :: pureSub
  end type

 contains

  impure subroutine impureSub(x)
    type(impureFinal), intent(in) :: x
  end subroutine

  pure subroutine pureSub(x)
    type(pureFinal), intent(in) :: x
  end subroutine

  subroutine s4()
    type(impureFinal), allocatable :: ifVar, ifvar1
    type(pureFinal), allocatable :: pfVar
    allocate(ifVar)
    allocate(ifVar1)
    allocate(pfVar)

    ! OK for an ordinary DO loop
    do i = 1,10
      if (i .eq. 1) deallocate(ifVar)
    end do

    ! OK to invoke a PURE FINAL procedure in a DO CONCURRENT
    ! This case does not work currently because the compiler's test for
    ! HasImpureFinal() in .../lib/Semantics/tools.cc doesn't work correctly
!    do concurrent (i = 1:10)
!      if (i .eq. 1) deallocate(pfVar)
!    end do

    ! Error to invoke an IMPURE FINAL procedure in a DO CONCURRENT
    do concurrent (i = 1:10)
          !ERROR: Deallocation of an entity with an IMPURE FINAL procedure caused by a DEALLOCATE statement not allowed in DO CONCURRENT
      if (i .eq. 1) deallocate(ifVar)
    end do

    do concurrent (i = 1:10)
      if (i .eq. 1) then
        block
          type(impureFinal), allocatable :: ifVar
          allocate(ifVar)
          ! Error here because exiting this scope causes the finalization of 
          !ifvar which causes the invocation of an IMPURE FINAL procedure
          !ERROR: Deallocation of an entity with an IMPURE FINAL procedure caused by block exit not allowed in DO CONCURRENT
        end block
      end if
    end do

    do concurrent (i = 1:10)
      if (i .eq. 1) then
        ! Error here because the assignment statement causes the finalization 
        ! of ifvar which causes the invocation of an IMPURE FINAL procedure
!ERROR: Deallocation of an entity with an IMPURE FINAL procedure caused by assignment not allowed in DO CONCURRENT
        ifvar = ifvar1
      end if
    end do
  end subroutine s4

end module m2
