Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

A pointer passed to DEALLOCATE points to an object that cannot be deallocated

pwl_b
Beginner
4,128 Views

Compiling (with ifort 13.1.1) and executing the following code

[fortran]

module tt

  type :: t
  end type t

  type :: t1
     class(t), pointer :: tp
   contains
     procedure :: set
     procedure :: unset
  end type t1

contains

  function t_new() result(r)
    type(t), pointer :: r
    allocate(r)
  end function t_new

  subroutine set(a, tp)
    class(t1) :: a
    class(t), target :: tp
    a%tp => tp
  end subroutine set

  subroutine unset(a)
    class(t1) :: a
    print *, "Dealocating"
    deallocate(a%tp)
    print *, "Deallocated"
  end subroutine unset

end module tt


program test

  use tt

  type(t1) :: a

  call a%set(t_new())
  call a%unset()

end program test

[/fortran]

I get

[plain]

 Dealocating
forrtl: severe (173): A pointer passed to DEALLOCATE points to an object that cannot be deallocated
Image              PC                Routine            Line        Source             
pointer            000000000046D7AE  Unknown               Unknown  Unknown
pointer            000000000046C246  Unknown               Unknown  Unknown
pointer            0000000000424FC2  Unknown               Unknown  Unknown
pointer            00000000004068BB  Unknown               Unknown  Unknown
pointer            00000000004050E6  Unknown               Unknown  Unknown
pointer            0000000000402C57  Unknown               Unknown  Unknown
pointer            0000000000402B3C  Unknown               Unknown  Unknown
libc.so.6          00007FB324690A15  Unknown               Unknown  Unknown
pointer            0000000000402A39  Unknown               Unknown  Unknown

[/plain]

but using gfortran yields

[plain]

 Dealocating
 Deallocated

[/plain]

Is this a bug?

Best,

Paweł Biernat

0 Kudos
24 Replies
Steven_L_Intel1
Employee
3,555 Views

Yes, it is a bug. I have escalated it as issue DPD200243572. A workaround is to have t_new return a class(t) pointer that is allocated as type(t), for example:

[fortran]
  function t_new() result(r)
   class(t), pointer :: r
    allocate(t::r)
  end function t_new
[/fortran]

0 Kudos
Lu__You
Beginner
3,350 Views

Hi Steven,

I am wondering how this type of function, what returns a pointer as result that is allocated locally, can avoid memory leak by proper deallocating. I have an application of such pattern in a recursive function. Many thanks.

You

0 Kudos
Steve_Lionel
Honored Contributor III
3,335 Views

@Lu__You , this is entirely the programmer's responsibility. Fortran doesn't give you any help here. 

0 Kudos
Lu__You
Beginner
3,331 Views

@Steve_Lionel

Thank you. Surely it is. But is it even possible in theory to deallocate such result pointers in Fortran? I would appreciate it if you have any suggestions.

0 Kudos
Steve_Lionel
Honored Contributor III
3,323 Views

Of course. Assuming you use pointer assignment and not intrinsic assignment:

 

program test
    implicit none
    integer, pointer :: p => null()
    
    p => f(3)
    print *, associated(p), p
    deallocate (p)
    print *, associated(p)
    
    contains
    
    function f(x)
    integer, pointer :: f
    integer, intent(in) :: x
    allocate (f)
    f = x
    return
    end function f
    
    end program test
    

 

 T           3
 F
0 Kudos
Lu__You
Beginner
3,314 Views

@Steve_Lionel 

Thank you for the quick response.

Sorry I forgot to make it clear that I was just using the intrinsic allocate function, something like:

recursive function returnScalar(meta) result(val)

class(*), pointer :: val

allocate(val, source=returnScalar(meta%next))

...

But it's also very helpful to confirm that such implementation can't avoid a leak. Thank you very much.

0 Kudos
Steve_Lionel
Honored Contributor III
3,302 Views

Yes, it CAN leak. It's up to you to make sure it doesn't. 

Your question is not really related to this thread, however.

0 Kudos
Lu__You
Beginner
3,295 Views

Apologies for digressing! But thanks a lot for the discussion!

0 Kudos
FortranFan
Honored Contributor II
3,269 Views
@Steve_Lionel wrote:
.. Your question is not really related to this thread, however.

@Yu__Lou wrote:
Apologies for digressing! But thanks a lot for the discussion!

 

@Lu__You ,

You're on the right track in thinking about memory leak issues in codes that do allocations in recursive functions the way you show in a brief snippet.

As implied by @Steve_Lionel comment, your concerns are worth for a different thread.

And indeed such a thread can involve code design aspects as well as modern Fortran language facilities.

A good place for such a discussion will be the Fortran Discourse site at:

https://fortran-lang.discourse.group/

I suggest you open up a thread at this Discourse site and share as much details regarding the code you have in mind with recursive functions, unlimited polymorphic object with POINTER attribute, and possible a linked list like derived type.

0 Kudos
Lu__You
Beginner
3,252 Views

@FortranFan 

Thank you for the suggestion. It's indeed a good idea to have some discussions there!--It's more about intrinsic limits of the current Fortran language standards.

In fact I am following the current thread because I'm having a similar difficulty of deallocating pointers to unlimited polymorphic components of a derived type. (It's another concern of memory leak!) It segfaults as of 17.0.2. Then I happened to see @Steve_Lionel's snippet code of returning a pointer in a similar concept of my memory-leaking implementation. Lol

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,127 Views

OOPS, I wrote the following before seeing the above post.

Ignore the following (as you are using TBP)

>> I'm having a similar difficulty of deallocating pointers to unlimited polymorphic components of a derived type.

The above states that you have an external (to the derived type) pointer to an allocatable within the derived type.

Should this component be ALLOCATABLE, it would also have to have the TARGET attribute .AND. the (external) pointer could not deallocate this component.

Should this component be POINTER (which is/was allocated), then the external pointer could perform the deallocate **** with your requirement to NULLIFY the component pointer, and all other pointers that may be pointing to that component.

You would be best served by creating a type bound function/subroutine to perform this.

Type-Bound Procedures (intel.com)

Jim Dempsey

0 Kudos
pwl_b
Beginner
3,555 Views

Thanks, I will use the workaround.

Paweł

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,555 Views

Steve,

Why is scoping required on the allocate?

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
3,555 Views

Jim, if you mean the t::, that specifies the dynamic type of the allocated polymorphic object. It is not strictly necessary in this case, as without it the declared type t is used anyway, but I like to put it in there for clarity when allocating a polymorphic object.

0 Kudos
Steven_L_Intel1
Employee
3,555 Views

An alternative workaround, and one I think is "more correct", is to make the tp argument to set a pointer rather than target. The problem is that anything could be a target and not necessarily something that was allocated. We need to look at this closer and it may be a while before we have an answer. But regardless, I think pointer is the right thing to use in set.

0 Kudos
IanH
Honored Contributor II
3,555 Views

In F2003 (specifically), can the value that results from the evaluation of an expression have the target attribute?

0 Kudos
Steven_L_Intel1
Employee
3,555 Views
No - "An entity with the TARGET attribute shall be a variable" (C555 in F2008) I discussed this with the developers and their position is that the current behavior is correct - doing a pointer assignment to something that isn't a pointer loses the ability to deallocate it later through that pointer, even if that thing was itself allocated. I am going to be out the next three weeks and will have to defer study of what the standard says about this until I get back.
0 Kudos
pwl_b
Beginner
3,555 Views

So is the statement

[fortran]

call a%set(t_new())

[/fortran]

valid in F2008 at all? Should I avoid using the result of a function as an argument this way and introduce temporary variable instead?

[fortran]

class(t), pointer :: tmp

tmp=>t_new()

call a%set(tmp)

[/fortran]

Paweł

0 Kudos
IanH
Honored Contributor II
3,554 Views

That specific statement is valid F2003/F2008, the problem is what happens afterwards, particularly when the set procedure finishes.  I think in F2003 the pointer association status of the tp component becomes undefined - this is because the dummy argument that it is pointed at is associated with an actual argument that is an expression, as part of the call that expression is evaluated, evaluation of an expression produces a value, as Steve says - a value cannot be a target, when you have a non-target actual associated with a target dummy the pointer association status of things pointed at the dummy becomes undefined when the procedure terminates.

In F2008 a variable can be a function reference that has a data pointer result (which I don't think ifort does yet, but I think gfortran does).  A variable can be a target - whatever the function result points at has to have had the target attribute.  So you have a target actual with a target dummy.  So things are ...umm... different.  After that - I get confused and give up.  Part of my confusion resulted in a post to c.l.f.

Edit to note that the first paragraph above is all wrong.  See interp F95/0074 case 3.

0 Kudos
S_2
Beginner
3,554 Views

Dear all,

Does anyone working at intel know the status of this issue? is there a plan to fix it?
I run into the same issue with the last version of the intel compiler (2019). Even though the workaround of using a class(t) pointer that is allocated as type(t) works, it makes some code harder to read and to write since a "select type" is needed when working on the pointer.

EDIT: my bad, I didn't understand the workaround properly, select type is indeed not needed.

0 Kudos
Reply