Bug 42112 - overloaded function with allocatable result problem
Summary: overloaded function with allocatable result problem
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 4.5.0
: P3 normal
Target Milestone: 4.7.4
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2009-11-19 22:20 UTC by mrestelli
Modified: 2016-11-22 11:59 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.7.4, 4.8.4
Last reconfirmed: 2009-11-20 14:22:49


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description mrestelli 2009-11-19 22:20:08 UTC
The attached code produces an error at runtime, however it seems fine
to me. Notice that there is no error accessing the function with the
specific name "g", while there is an error when using the generic name
"gen_g".

gfortran --version
GNU Fortran (GCC) 4.5.0 20091105 (experimental)

gfortran ./abc.f90 -o abc

./abc 
At line 23 of file ./abc.f90
Fortran runtime error: Attempting to allocate already allocated array 'j'


module mod_m
 implicit none
 public :: f
 private

 interface gen_g
   module procedure g
 end interface

contains

 pure function f() result(i)
  integer :: i
  integer, allocatable :: loc_ar(:)
   allocate(loc_ar(1))
   loc_ar = gen_g() ! does not work
   !loc_ar = g() ! no problem here
   deallocate(loc_ar)
 end function f

 pure function g() result(j)
  integer, allocatable :: j(:)
   allocate( j(1) )
   j = 2
 end function g

end module mod_m

!--------------------------

program abc_main

 use mod_m, only: f
 implicit none
 integer :: p
  p = f()

end program abc_main
Comment 1 kargls 2009-11-19 23:55:53 UTC
If you remove 

   allocate(loc_ar(1))
 
   deallocate(loc_ar)

in function f(), the code compiles and runs in either case.
Comment 2 Steve Kargl 2009-11-20 00:20:22 UTC
Subject: Re:  overloaded function with allocatable result problem

If the code is compiled with -fdump-tree-original one
immediately see the cause of the runtime error.  Eliminating
the common code in the dumps, I find this code

  pure function f() result(i)
    integer :: i
    integer, allocatable :: loc_ar(:)
    allocate(loc_ar(1))
    loc_ar = gen_g() ! does not work
    deallocate(loc_ar)
    i = 1
  end function f

generates

  f ()
  {
    g (&loc_ar);
  }

loc_ar has been allocated and the pointer to it is passed
to g() where is becomes associated with the result variable j.
You then try to allocate j, which is already allocated.


When you call g() directly in 

   pure function f() result(i)
    integer :: i
    integer, allocatable :: loc_ar(:)
    allocate(loc_ar(1))
    loc_ar = g() ! no problem here
    deallocate(loc_ar)
    i = 1
   end function f

the dump shows

  f ()
  {
    {
      integer(kind=8) D.1398;
      struct array1_integer(kind=4) atmp.1;
      integer(kind=8) D.1393;
      integer(kind=8) D.1392;
      integer(kind=8) D.1391;
      integer(kind=4)[0:] * restrict D.1390;

      D.1390 = (integer(kind=4)[0:] * restrict) loc_ar.data;
      D.1391 = loc_ar.offset;
      D.1392 = loc_ar.dim[0].lbound;
      D.1393 = loc_ar.dim[0].ubound;
      atmp.1.dtype = 265;
      atmp.1.data = 0B;
      atmp.1.offset = 0;
      g (&atmp.1);

g() is called with a unallocated temporary variable, so
your allocation in g() works.  The next several lines
copy atmp into loc_ar.

      D.1398 = NON_LVALUE_EXPR <D.1392>;
      {
      integer(kind=8) S.2;

      S.2 = 0;
      while (1)
        {
          if (atmp.1.dim[0].ubound - atmp.1.dim[0].lbound < S.2) goto L.2;
          (*D.1390)[(S.2 + D.1398) + D.1391]
                   = (*(integer(kind=4)[0:] * restrict) atmp.1.data)[S.2];
          S.2 = S.2 + 1;
        }
      L.2:;
    }

Here, the temporary variable is destroyed.

    {
      void * D.1397;

      D.1397 = (void *) atmp.1.data;
      if (D.1397 != 0B)
        {
          __builtin_free (D.1397);
        }
    }
  }
}

I need to go read the Fortran standard to determine the
semantics of a function returning an allocatable entity.

Comment 3 Tobias Burnus 2009-11-20 14:22:48 UTC
  pure function f() result(i)
    integer :: i
    integer, allocatable :: loc_ar(:)
    allocate(loc_ar(1))
    loc_ar = gen_g() ! does not work

That looks OK if gen_g returns a size-1 array or a scalar - and with Fortran 2003 it should also be valid if the array has a different size (automatic (re)allocation on assignment).

> generates
>
>  f ()
>  {
>    g (&loc_ar);
>  }
>
> loc_ar has been allocated and the pointer to it is passed
> to g() where is becomes associated with the result variable j.
> You then try to allocate j, which is already allocated.

That sounds completely wrong for Fortran 95 but also for 2003. There is absolutely no valid reason to mess around with the LHS argument, i.e. C_LOC(loc_ar(1)) should be the same before and after the assignment (unless in F2003 if the array size of LHS/RHS does not match or if loc_ar was before not allocated).

To me it sounds as if there is the problem that the attributes of the specific function belonging to the generic function are not properly used.

That is: expr->symtree->n.sym->attr vs. expr->value.function.esym->attr. (Cf. PR 41777 )
Comment 4 Paul Thomas 2010-11-21 11:41:13 UTC
(In reply to comment #0)

> Fortran runtime error: Attempting to allocate already allocated array 'j'
> 
....snip....

>    allocate(loc_ar(1))
>    loc_ar = gen_g() ! does not work
>    !loc_ar = g() ! no problem here
>    deallocate(loc_ar)
>  end function f
> 
>  pure function g() result(j)
>   integer, allocatable :: j(:)
>    allocate( j(1) )
>    j = 2
>  end function g

This looks correct to me, unless F2003 forces a reallocation in ALLOCATE, when frealloc-lhs is in effect?

I will look tonight, unless somebody beats me to it.

If the first allocate is omitted, the code compiles..... but not with my patch. ****shucks***

Paul
Comment 5 Paul Thomas 2010-11-25 05:58:03 UTC
(In reply to comment #4)

> 
> This looks correct to me, unless F2003 forces a reallocation in ALLOCATE, when
> frealloc-lhs is in effect?

This is specifically not the case.  The standard says, unconditionally, that the ALLOCATE statement generates and error with an already allocated array.

> 
> I will look tonight, unless somebody beats me to it.
> 
> If the first allocate is omitted, the code compiles..... but not with my patch.
> ****shucks***

I have fixed it for my rellocate on assign patch but a question remains that is at the core of the original problem:

At trans-array.c:6958
  /* Dummy, use associated and result variables don't need anything special.  */
  if (sym->attr.dummy || sym->attr.use_assoc || sym->attr.result)
    {
      gfc_add_init_cleanup (block, gfc_finish_block (&init), NULL_TREE);
      return;
    }

ie. deallocation is not done on procedure entry for results.  I believe that this is incorrect for gfortran's pass by reference handling of function results.

Cheers

Paul
Comment 6 Tobias Burnus 2010-11-25 08:17:26 UTC
(In reply to comment #5)
> at the core of the original problem:
> 
> At trans-array.c:6958
> ie. deallocation is not done on procedure entry for results.  I believe that
> this is incorrect for gfortran's pass by reference handling of function
> results.

Actually one needs to be careful: For
  a = f()
which is transferred as
  f(&a)
the "a" may not be deallocated as otherwise pointers to "a" may break. One probably needs:

a) If "a" is no TARGET:
  deallocate (a)
  f(&a)

b) If "a" is a TARGET
  f(tmp)
  memcpy(a,tmp)
Comment 7 Tobias Burnus 2010-11-25 08:54:10 UTC
(In reply to comment #6)
> One probably needs:
> 
> a) If "a" is no TARGET:
>   deallocate (a)
>   f(&a)

... which has with -fno-realloc-lhs issues when bound checking is enabled. (I do not know whether we should care.)

> b) If "a" is a TARGET
>   f(tmp)
>   memcpy(a,tmp)

... while this one can be optimized with realloc-lhs to a deallocate + pointer assignment if the size does not fit (rather than a malloc plus deep copying).
Comment 8 Paul Thomas 2010-11-28 11:17:03 UTC
Tobias,

(In reply to comment #7)
> (In reply to comment #6)
> > One probably needs:
> > 
> > a) If "a" is no TARGET:
> >   deallocate (a)
> >   f(&a)

At present, this is not done.  I guess that it has to be done by the caller, rather than the callee, simply because of the issue with TARGET below.

> 
> ... which has with -fno-realloc-lhs issues when bound checking is enabled. (I
> do not know whether we should care.)

Surely, we should just not do the deallocate with -fno-realloc-lhs?

> 
> > b) If "a" is a TARGET
> >   f(tmp)
> >   memcpy(a,tmp)
> 
> ... while this one can be optimized with realloc-lhs to a deallocate + pointer
> assignment if the size does not fit (rather than a malloc plus deep copying).

I guess that this makes sense.  It is very easily done.

I'll put this on the TODO list, for fairly early implementation.  I'll see what I can do with scalars, first.

Paul
Comment 9 Dominique d'Humieres 2015-04-17 17:49:21 UTC
This seems to have been fixed at least for 4.8.4.
Comment 10 Mikael Morin 2015-08-07 20:46:16 UTC
(In reply to Dominique d'Humieres from comment #9)
> This seems to have been fixed at least for 4.8.4.

Hum, if one changes the comment #0 to output the value of p, it outputs 0 instead of 2
Comment 11 janus 2016-11-22 11:59:10 UTC
> (In reply to Dominique d'Humieres from comment #9)
> > This seems to have been fixed at least for 4.8.4.

Confirmed. I don't get any runtime error with 4.7.4 either.


(In reply to Mikael Morin from comment #10) 
> Hum, if one changes the comment #0 to output the value of p, it outputs 0
> instead of 2

That's correct, because the result 'i' of the function 'f' is not being set anywhere.


Obvious this issue has been fixed. Closing.