Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.

What is the effect of indirect recursion on ELEMENTAL procedures, especially finalizers?

FortranFan
Honored Contributor III
889 Views

The following simple code involving a linked list compiles with no errors or warnings with compiler 2015, update 2.  It seems to execute without any run-time exceptions as well.

However, you will notice the code in module m invokes a finalizer for the derived type for linked list (node_t) that has indirect recursion via clear_node_t; this is to work around the restriction in the standard that both RECURSIVE and ELEMENTAL attributes cannot be applied with subroutines.

What are the risks associated with such a workaround?

Colud the compiler do a static allocation (non-automatic) for allocatable array foo in program p and could the indirect recursion then possibly lead to a failure of some sort under some circumstances?  Note, however, I have not noticed any such issues - no memory leaks are detected nor any other run-time exceptions; I tried /heaparrays0 also.

module m

   implicit none

   private

   type, public :: node_t

      private

      type(node_t), pointer         :: m_child => null()
      character(len=:), allocatable :: m_key

   contains

      private

      procedure, pass(this) :: clear => clear_node_t
      final :: clean_node_t

      procedure, pass(this), public :: put => put_node_t

   end type node_t

contains

   recursive pure subroutine put_node_t(this, key)

      class(node_t), intent(inout) :: this
      character(len=*), intent(in) :: key

      if (allocated(this%m_key)) then
         if (this%m_key /= key) then
            if (.not. associated(this%m_child)) then
               allocate(this%m_Child)
            end if
            call put_node_t(this%m_Child, key)
         end if
      else
         this%m_key = key
      end if

      return

   end subroutine put_node_t

   pure recursive subroutine clear_node_t(this)

      class(node_t), intent(inout) :: this

      if (allocated(this%m_key)) then
         deallocate(this%m_key)
      end if

      if (associated(this%m_child)) then
         deallocate(this%m_child)
         this%m_child => null()
      end if

      return

   end subroutine clear_node_t

   pure elemental subroutine clean_node_t(this)

      type(node_t), intent(inout) :: this

      call clear_node_t(this)

   end subroutine clean_node_t

end module m
program p

   use m, only : node_t

   implicit none

   type(node_t), allocatable :: foo(:)
   integer :: istat

   allocate( node_t :: foo(2), stat=istat)
   if (istat /= 0) then
      print *, " istat = ", istat
      stop
   end if

   call foo(1)%put("red")
   call foo(1)%put("green")

   call foo(2)%put("circle")
   call foo(2)%put("triangle")

   deallocate(foo, stat=istat)
   print *, " deallocate(foo): istat = ", istat

   stop

end program p

 

I understand Fortran 2015 lifts the restriction on RECURSIVE and ELEMENTAL but that is for the long-term.  With the current standard, is such an indirect recursion a concern?  If yes, is there any way to make the coder aware of it?

Thanks,

     

0 Kudos
3 Replies
Steven_L_Intel1
Employee
889 Views

I sort-of followed the discussion in comp.lang.fortran but wasn't paying a lot of attention to it. In the example here, the compiler will likely use static allocation for the descriptor of foo, but the data will always be on the heap. I don't see what the problem with static allocation would be since the main program is never reentered. Am I missing something?

The finalizer would be called for each allocation of type node_t when it is deallocated.

That the finalizer could get recursively entered is a possible problem, but since there are no local variables in clean_node_t, the risk seems minimal. If however you had local variables in clean_node_t and it was recursively entered, that would be problematic as some of them could involve static allocation.

My take on this is that you're always taking a risk when you hide things from the compiler (or the standard). I don't see any feasible way to warn about this, especially if separate sources are involved.

0 Kudos
FortranFan
Honored Contributor III
889 Views

Steve Lionel (Intel) wrote:

..

My take on this is that you're always taking a risk when you hide things from the compiler (or the standard). I don't see any feasible way to warn about this, especially if separate sources are involved.

Point well-taken.

By the way, draft Fortran 2015 says, "Procedures, including elemental procedures, can be invoked recursively by default; the RECURSIVE keyword is advisory only. The NON_RECURSIVE keyword specifies that a procedure is not recursive."

So is this change to recursive by default fully accepted in the next standard, or is it still up in the air in terms of votes?

 

0 Kudos
Steven_L_Intel1
Employee
889 Views

The straw vote on recursion by default (paper 14-179, a UK proposal) was 12-1 with two undecided. I don't expect opposition to it.

0 Kudos
Reply