Bug 60913 - [OOP] Memory leak with allocatable polymorphic function result (in type-bound operator)
Summary: [OOP] Memory leak with allocatable polymorphic function result (in type-bound...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks: 86754
  Show dependency treegraph
 
Reported: 2014-04-21 10:12 UTC by Alberto
Modified: 2019-09-03 16:56 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2014-04-21 00:00:00


Attachments
Module with an overloaded operator with a deferred routine (1.53 KB, text/plain)
2014-04-21 10:12 UTC, Alberto
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alberto 2014-04-21 10:12:49 UTC
Created attachment 32648 [details]
Module with an overloaded operator with a deferred routine

When overloading an operator with a deferred procedure, gfortran leaks (i.e. seem that does not free) memory.

I attach an example code that leaks around 60KB in my system with the following compilers:


GNU Fortran (FreeBSD Ports Collection) 4.6.4
Copyright (C) 2011 Free Software Foundation, Inc.

GNU Fortran (FreeBSD Ports Collection) 4.8.3 20140220 (prerelease)
Copyright (C) 2013 Free Software Foundation, Inc.


GNU Fortran (FreeBSD Ports Collection) 4.9.0 20140223 (experimental)
Copyright (C) 2014 Free Software Foundation, Inc.

Thanks,

A.
Comment 1 Thomas Koenig 2014-04-21 12:38:34 UTC
Valgrind shows:

ig25@linux-fd1f:~/Krempel/Leak> valgrind  --leak-check=full ./a.out
==4355== Memcheck, a memory error detector
==4355== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==4355== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==4355== Command: ./a.out
==4355== 
g:      2.3000E-01  4.6000E-01  6.9000E-01  9.2000E-01
g^-1:   3.2000E-01  6.4000E-01  9.6000E-01  1.2800E+00
gg^-1: -2.0608E+00  2.9440E-01  4.4160E-01  5.8880E-01
==4355== 
==4355== HEAP SUMMARY:
==4355==     in use at exit: 64,096 bytes in 2,003 blocks
==4355==   total heap usage: 2,028 allocs, 25 frees, 83,275 bytes allocated
==4355== 
==4355== 63,968 bytes in 1,999 blocks are definitely lost in loss record 5 of 5
==4355==    at 0x4C277AB: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==4355==    by 0x401109: __group_su2_MOD_prodsu2 (foo.f90:202)
==4355==    by 0x4017AE: MAIN__ (foo.f90:289)
==4355==    by 0x401A0C: main (foo.f90:268)
==4355== 
==4355== LEAK SUMMARY:
==4355==    definitely lost: 63,968 bytes in 1,999 blocks
==4355==    indirectly lost: 0 bytes in 0 blocks
==4355==      possibly lost: 0 bytes in 0 blocks
==4355==    still reachable: 128 bytes in 4 blocks
==4355==         suppressed: 0 bytes in 0 blocks
==4355== Reachable blocks (those to which a pointer was found) are not shown.
==4355== To see them, rerun with: --leak-check=full --show-reachable=yes
==4355== 
==4355== For counts of detected and suppressed errors, rerun with: -v
==4355== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

where line 202 is

    Allocate(SU2::g3)

so the issue is with the allocatable function result of Prodsu2.
Comment 2 Dominique d'Humieres 2014-04-21 12:47:56 UTC
Confirmed from 4.6 to trunk (4.10). It may be a duplicate of pr55603.
Comment 3 janus 2015-01-03 12:54:53 UTC
(In reply to Dominique d'Humieres from comment #2)
> It may be a duplicate of pr55603.

Pretty much. But the case here is a bit more complicated, since the result is polymorphic and the function is bound to a type-bound operator.
Comment 4 Chris MacMackin 2017-01-24 17:26:25 UTC
Has there been any progress on this bug? It is making a large piece of scientific software I have written unusable for decent resolution simulations.
Comment 5 Chris MacMackin 2017-01-24 17:48:46 UTC
Has there been any progress on this bug? It is making a large piece of scientific software I have written unusable for decent resolution simulations. I'm running gfortran 6.1.0, compiled with OpenCoarrays support.

Also, I've reduced the size of the reproducer:


Module Groups

  Type, abstract :: Group
   CONTAINS
     Generic, Public :: Operator   (*) => prod
     Generic, Public :: Assignment (=) => equal

     Procedure (gpr), deferred :: prod
     Procedure (geq), deferred :: equal
  End type Group

  Type, extends (Group), abstract :: GaugeGroup
  End type GaugeGroup

  Abstract Interface
     Function gpr(a, b)
       Import :: Group

       Class (Group), Intent (in) :: a, b
       Class (Group), Allocatable :: gpr
     End Function gpr
  End Interface

  Abstract Interface
     Subroutine geq(a, b)
       Import :: Group

       Class (Group), Intent (in)  :: b
       Class (Group), Intent (out) :: a
     End Subroutine geq
  End Interface

End Module Groups


MODULE Group_SU2

  USE Groups

  IMPLICIT NONE

  ! Represent SU(2) element through quaternions
  ! (a_0, a_1, a_2, a_3)
  Type, Extends (GaugeGroup) :: SU2
     Real (kind=8) :: Comp(0:3)
   CONTAINS
     Procedure :: prod    => prodsu2
     Procedure :: equal   => equalSU2
  End Type SU2
  
CONTAINS

! *******************************************
! *
  Subroutine EqualSU2(a, b)
! *
! *******************************************
! * Equals g1 to g2
! *******************************************

    Class (SU2), Intent (out) :: a
    Class (Group), Intent (in) :: b
    
    select type (b)
    class is (SU2)
       a%Comp(0:3) = b%Comp(0:3)
    class default
       error stop
    End select
          
    Return
  End Subroutine EqualSU2

! *******************************************
! *
  Function Prodsu2(a,b) Result(g3)
! *
! *******************************************
! * Multiplies g1 by g2
! *******************************************

    Class (SU2),   Intent (in) :: a
    Class (Group), Intent (in) :: b
    Class (Group), allocatable :: g3

    Allocate(SU2::g3)
    select type (b)
    class is (SU2)
       select type (g3)
       class is (SU2)
          g3%Comp(0) = a%Comp(0)*b%Comp(0) - &
               &       Dot_Product(a%Comp(1:3),b%Comp(1:3))
          
          g3%Comp(1) = a%Comp(0)*b%Comp(1) + a%Comp(1)*b%Comp(0) &
               & + a%Comp(2)*b%Comp(3) - a%Comp(3)*b%Comp(2)
          
          g3%Comp(2) = a%Comp(0)*b%Comp(2) - a%Comp(1)*b%Comp(3) &
               & + a%Comp(2)*b%Comp(0) + a%Comp(3)*b%Comp(1)
          
          g3%Comp(3) = a%Comp(0)*b%Comp(3) + a%Comp(1)*b%Comp(2) &
               & - a%Comp(2)*b%Comp(1) + a%Comp(3)*b%Comp(0)
       end select
    class default
       error stop
    end select

    Return
  End Function Prodsu2

End MODULE GROUP_SU2


Program Testoo

  USE Groups
  USE Group_SU2

  type(SU2) :: g1, g2, g3

  ForAll (I=0:3) g1%comp(I) = 0.23_8*(I+1)
  ForAll (I=0:3) g2%comp(I) = 0.32_8*(I+1)

  Do I = 1, 2000
     g3=g1*g2
  End Do

  Stop
End Program Testoo
Comment 6 Damian Rouson 2017-01-25 07:50:42 UTC
I don't have any specific knowledge of it being fixed, but there have been two releases since 6.1.0: the latest is 6.3.0 and 7.1.0 is expected to be released soon so the current trunk is a nearly releasable state.  In case it helps, both 6.3.0 and a relatively recent build of 7.0.0 are available in the virtual machine here: www.sourceryinstitute.org/store.

On a separate note, I am in the process of trying to decide about whether I will continue to use allocatable polymorphic function results because they are not allowed in pure functions of which I'm a big fan both for code clarity and potential performance benefits and especially for the ease with which one can parallelize expressions comprised of operators implemented as pure functions.

Last, FORALL will be declared obsolescent in Fortran 2015.  In the example that you posted, I recommend considering DO CONCURRENT as a replacement wherever possible.

Damian
Comment 7 Chris MacMackin 2017-01-25 08:11:30 UTC
Damian, can you tell me where in the standard allocatable polymorphic results are prohibited with pure functions? I've been using them that way in my code, which compiles without issue using gfortran. I haven't tested it with another compiler although coincidentally I was planning to do that later today.

I am aware of forall being declared deprecated. The example I posted was just a shortened version of that used at the start of this report. I don't use forall in my own code.
Comment 8 Jerry DeLisle 2017-01-25 18:43:59 UTC
I tested this with latest trunk, gfortran 7.0.0, and valgrind and the issue is still there. I guess the implicit allocation that happens with:

type(SU2) :: g1, g2, g3

is not being implicitly deallocated. Does it require a finalization procedure?

or would a finalization procedure be a work around?
Comment 9 Chris MacMackin 2017-01-25 18:51:15 UTC
Last I heard, gfortran still doesn't invoke finalisation in this situation. In any case, while (in my main code) I could certainly reduce the volume of leaked memory via finalisation (by deallocating arrays contained in the derived type variable), it wouldn't work to deallocate the variable itself.
Comment 10 Ian Harvey 2017-01-28 02:38:40 UTC
The issue appears to be that code generated by the compiler does not deallocate the polymorphic allocatable function result when the innermost executable construct containing the function reference terminates (F2008 6.7.3.2p5).  This only appears to be the case for when the function result is polymorphic.

Clarifying questions here and elsewhere - this is something that only the compiler can arrange (it must be "automatic"), as the allocatable characteristic of the function result is only accessible to the compiler in the calling scope.  The user does not need to arrange anything - there is nothing they can arrange.  The presence or absence of a finalizer is not relevant - a finalizer cannot explicitly deallocate the object that it is finalizing, it can only explicitly deallocate allocatable sub-objects of the object being finalized.  The type declaration statements mentioned in #8 are not particularly relevant, the issue is with the handling of function references.

This may be a simpler example:

MODULE m
  IMPLICIT NONE
  
  TYPE :: t
    INTEGER :: comp
  END TYPE t
CONTAINS
  FUNCTION f(i)
    INTEGER, INTENT(IN) :: i
    CLASS(t), ALLOCATABLE :: f
    ALLOCATE(f)
    f%comp = i
  END FUNCTION f
  
  SUBROUTINE proc(arg)
    CLASS(t), INTENT(IN) :: arg
    PRINT *, arg%comp
  END SUBROUTINE proc
END MODULE m

PROGRAM p
  USE m
  IMPLICIT NONE
  INTEGER :: i
  DO i = 1, 100
    CALL proc(f(i))
    ! The function result should be deallocated after 
    ! execution of the above statement completes.
  END DO
END PROGRAM p

~~~

$ gfortran -g -v 2017-01-28\ alloc2.f90 && valgrind --leak-check=full ./a.out
Driving: gfortran -g -v 2017-01-28 alloc2.f90 -l gfortran -l m -shared-libgcc
Using built-in specs.
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=/home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: .././src/configure --prefix=/home/MEGMS2/ian/usr/gcc-7.0.1 --enable-languages=c,c++,fortran,lto --enable-libgomp --enable-checking=release
Thread model: posix
gcc version 7.0.1 20170120 (experimental) (GCC)
COLLECT_GCC_OPTIONS='-g' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/f951 2017-01-28 alloc2.f90 -quiet -dumpbase 2017-01-28 alloc2.f90 -mtune=generic -march=x86-64 -auxbase 2017-01-28 alloc2 -g -version -fintrinsic-modules-path /home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/finclude -o /tmp/ccRZA3YY.s
GNU Fortran (GCC) version 7.0.1 20170120 (experimental) (x86_64-pc-linux-gnu)
        compiled by GNU C version 7.0.1 20170120 (experimental), GMP version 6.1.1, MPFR version 3.1.5, MPC version 1.0.2, isl version 0.15
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
GNU Fortran2008 (GCC) version 7.0.1 20170120 (experimental) (x86_64-pc-linux-gnu)
        compiled by GNU C version 7.0.1 20170120 (experimental), GMP version 6.1.1, MPFR version 3.1.5, MPC version 1.0.2, isl version 0.15
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
COLLECT_GCC_OPTIONS='-g' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/ccXmIutE.o /tmp/ccRZA3YY.s
GNU assembler version 2.26.1 (x86_64-redhat-linux) using BFD version version 2.26.1-1.fc25
Reading specs from /home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/../../../../lib64/libgfortran.spec
rename spec lib to liborig
COLLECT_GCC_OPTIONS='-g' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
COMPILER_PATH=/home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/:/home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/:/home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/:/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/:/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/
LIBRARY_PATH=/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/:/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-g' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/collect2 -plugin /home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/liblto_plugin.so -plugin-opt=/home/MEGMS2/ian/usr/gcc-7.0.1/libexec/gcc/x86_64-pc-linux-gnu/7.0.1/lto-wrapper -plugin-opt=-fresolution=/tmp/ccMep7Yj.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lquadmath -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /lib/../lib64/crt1.o /lib/../lib64/crti.o /home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/crtbegin.o -L/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1 -L/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/../../.. /tmp/ccXmIutE.o -lgfortran -lm -lgcc_s -lgcc -lquadmath -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /home/MEGMS2/ian/usr/gcc-7.0.1/lib/gcc/x86_64-pc-linux-gnu/7.0.1/crtend.o /lib/../lib64/crtn.o
COLLECT_GCC_OPTIONS='-g' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
==435== Memcheck, a memory error detector
==435== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==435== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==435== Command: ./a.out
==435==
           1
...
         100
==435==
==435== HEAP SUMMARY:
==435==     in use at exit: 400 bytes in 100 blocks
==435==   total heap usage: 119 allocs, 19 frees, 12,408 bytes allocated
==435==
==435== 400 bytes in 100 blocks are definitely lost in loss record 1 of 1
==435==    at 0x4C2DB9D: malloc (vg_replace_malloc.c:299)
==435==    by 0x400A20: __m_MOD_f (2017-01-28 alloc2.f90:11)
==435==    by 0x400A7D: MAIN__ (2017-01-28 alloc2.f90:26)
==435==    by 0x400AD4: main (2017-01-28 alloc2.f90:22)
==435==
==435== LEAK SUMMARY:
==435==    definitely lost: 400 bytes in 100 blocks
==435==    indirectly lost: 0 bytes in 0 blocks
==435==      possibly lost: 0 bytes in 0 blocks
==435==    still reachable: 0 bytes in 0 blocks
==435==         suppressed: 0 bytes in 0 blocks
==435==
==435== For counts of detected and suppressed errors, rerun with: -v
==435== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)