Bug 52102 - [OOP] Wrong result with ALLOCATE of CLASS components with array constructor SOURCE-expr
Summary: [OOP] Wrong result with ALLOCATE of CLASS components with array constructor S...
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 4.7.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Keywords: wrong-code
Depends on:
Reported: 2012-02-03 07:48 UTC by Tobias Burnus
Modified: 2012-02-05 20:06 UTC (History)
2 users (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed:


Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Burnus 2012-02-03 07:48:38 UTC
The following program, which is an extension of testsuite/gfortran.dg/class_48.f90 and thus PR 51972 fails to correctly set the value via SOURCE:

  allocate (two%a(2), source=[t(4), t(6)])

Should give for two%a(1)%x and two%a(2)%x the values 4 and 6, but one gets 0 0. If one uses not a class component but a class variable, the result is 4 0.

There is a similar PR for polymorphic sources, cf. PR 51864.

subroutine test3 ()
  type t
    integer :: x
  end type t

  type t2
    class(t), allocatable :: a(:)
  end type t2

  type(t2) :: one, two

  one = two
  if (allocated (one%a)) call abort ()

  allocate (two%a(2), source=[t(4), t(6)])
  one = two
  if (.not.allocated (one%a)) call abort ()

  print *, one%a(1)%x
  print *, one%a(6)%x
  if ((one%a(1)%x /= 4)) call abort ()
  if ((one%a(2)%x /= 6)) call abort ()

  deallocate (two%a)
  one = two
  if (allocated (one%a)) call abort ()
end subroutine test3
Comment 1 Tobias Burnus 2012-02-03 08:22:20 UTC
(In reply to comment #0)
> Should give for two%a(1)%x and two%a(2)%x the values 4 and 6, but one gets
> 0 0. If one uses not a class component but a class variable, the result
> is 4 0.

Correction: The latter works "4 6", thus, only CLASS components are affected.

>   print *, one%a(1)%x
>   print *, one%a(6)%x

The last line should use "(2)" not "(6)". [It only should print "6" at run time.]
Comment 2 Tobias Burnus 2012-02-03 13:21:43 UTC
From the dump:

 two.a._data.data = (void * restrict) __builtin_malloc (8);
 __builtin_memset (two.a._data.data, 0, 8);
 two.a._data.offset = -1;
   struct t D.1891;
     = (*(struct t[0:] * restrict) two.a._data.data)[two.a._data.offset + 2];

The "D.1891 = " line looks odd: One should get the address of "two.a._data" and not the value. Additionally, one gets the address of element "[-1]" which is invalid.

Also the loop is odd: One iterates through the elements of the SOURCE-expr, but one always writes to the same dst (here: the temporary variable D.1893):

            while (1)
                if (S.6 > 1) goto L.1;
                  struct t D.1893;

                  D.1893 = (*(struct t[2] * restrict) atmp.1.data)[S.6];
                  __vtab_MAIN___T._copy (&D.1893, &D.1891);

By contrast, for:

  class(t), allocatable :: two(:)
  allocate (two(2), source=[t(4), t(6)])

One has in the loop

   D.1892 = (*(struct t[2] * restrict) atmp.1.data)[S.6];
   __vtab_MAIN___T._copy (&D.1892,
       (struct t *) D.1882
                    + (sizetype) (((S.6 + 1) + D.1883)
                      * (integer(kind=8)) two._vptr->_size));

  struct t[0:] * restrict D.1882;
  D.1882 = (struct t[0:] * restrict) two._data.data;
Comment 3 Tobias Burnus 2012-02-03 13:37:39 UTC
Related issue with MOLD=, here one gets with "x = 5" default initialization and

  allocate (two%a(8), mold=t(4))
  print '(*(i2))', two%a(:)%x
the result:
 0 0 0 0 0 0 0 5

That is: only the last element gets the value.

From the dump:
   two.a._data.offset = -1;
     struct t t.2;
     t.2.x = 5;
     D.1886 = t.2;
     __vtab_MAIN___T._copy (&D.1886,
         &(*(struct t[0:] * restrict) two.a._data.data)[two.a._data.offset + 8]);

Note the constant offset of "8", which matches the ubound. Note further that the _copy construct is *not* in a scalarizing loop but only called once for the last element.

 * * *

The following looks odd:

gfc_trans_allocate (gfc_code * code)
      if (code->expr3 && !code->expr3->mold)
              /* Do a polymorphic deep copy.  */
              actual->next->expr = gfc_copy_expr (al->expr);
              dataref = actual->next->expr->ref;
              if (dataref->u.c.component->as)

The latter makes no sense in this case as the base object is not the class container. We need to access the last component ref on the ref list.
Comment 4 Paul Thomas 2012-02-05 19:56:16 UTC
Author: pault
Date: Sun Feb  5 19:56:09 2012
New Revision: 183915

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=183915
2012-02-05  Paul Thomas  <pault@gcc.gnu.org>

	* trans-array.c (gfc_array_allocate): Zero memory for all class
	array allocations.
	* trans-stmt.c (gfc_trans_allocate): Ditto for class scalars.

	PR fortran/52102
	* trans-stmt.c (gfc_trans_allocate): Before correcting a class
	array reference, ensure that 'dataref' points to the _data
	component that is followed by the array reference..

2012-02-05  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/52102
	* gfortran.dg/class_48.f90 : Add test of allocate class array
	component with source in subroutine test3.  Remove commenting
	out in subroutine test4, since branching on unitialized variable
	is now fixed (no PR for this last.).

Comment 5 Paul Thomas 2012-02-05 20:06:08 UTC
Fixed on trunk.

Thanks for the report