Bug 45516 - [F08] allocatable compontents of recursive type
Summary: [F08] allocatable compontents of recursive type
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 4.6.0
: P3 normal
Target Milestone: ---
Assignee: Paul Thomas
URL:
Keywords: rejects-valid
Depends on:
Blocks: F2008
  Show dependency treegraph
 
Reported: 2010-09-03 09:57 UTC by janus
Modified: 2020-10-21 12:39 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-02-03 22:03:06


Attachments
Patch, to be submitted, for this PR. (5.84 KB, patch)
2016-10-23 09:03 UTC, Paul Thomas
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description janus 2010-09-03 09:57:19 UTC
John Reid, The new features of Fortran 2008, chapter 5.3:

A recursive type is permitted to be based on allocatable components. Here is a simple example of a type that holds a stack:

type entry
  real :: value
  integer :: index
  type(entry), allocatable :: next
end type entry

[...]

For this gfortran currently gives:

  type(entry), allocatable :: next
                                  1
Error: Component at (1) must have the POINTER attribute
Comment 1 janus 2010-09-03 09:59:02 UTC
Here is a patch to accept the type declaration in comment #0:

Index: decl.c
===================================================================
--- decl.c      (revision 163798)
+++ decl.c      (working copy)
@@ -1494,11 +1494,11 @@ build_struct (const char *name, gfc_charlen *cl, g
   gfc_component *c;
   gfc_try t = SUCCESS;
 
-  /* F03:C438/C439. If the current symbol is of the same derived type that we're
-     constructing, it must have the pointer attribute.  */
+  /* F08:C440. If the current symbol is of the same derived type that we're
+     constructing, it must be POINTER or ALLOCATABLE.  */
   if ((current_ts.type == BT_DERIVED || current_ts.type == BT_CLASS)
       && current_ts.u.derived == gfc_current_block ()
-      && current_attr.pointer == 0)
+      && !current_attr.pointer && !current_attr.allocatable)
     {
       gfc_error ("Component at %C must have the POINTER attribute");
       return FAILURE;
Comment 2 Tobias Burnus 2010-09-03 12:12:25 UTC
(In reply to comment #1)
> Here is a patch to accept the type declaration in comment #0:

Well, you at least need to use gfc_notify_std(GFC_STD_F2008, ... And one should make sure that the allocation/clean up works as expected.

 * * *

As follow up to the initial implementation, one should implement at some point the following, which does not do a deep copy but simply appends the old data. (Example from the mentioned article.)

type entry
  real :: value
  integer :: index
  type(entry), allocatable :: next
end type entry

type (entry), allocatable :: top
top = entry ( new_value, new_index, top )

(The last line assume reallocate on assignment; I think it already works for derived types with structure constructor; I know that it does not yet work for intrinsic types - not does it work on other assignment than structure constructors.)

The classical way to append a new item is the following, which should avoid a deep copy (check!):

  type (entry), allocatable :: top, temp
  temp = entry ( new_value, new_index, temp )
  call move_alloc(top,temp%next)
  call move_alloc(temp,top)
Comment 3 janus 2011-02-03 22:03:06 UTC
(In reply to comment #2)
> (In reply to comment #1)
> > Here is a patch to accept the type declaration in comment #0:
> 
> Well, you at least need to use gfc_notify_std(GFC_STD_F2008, ...


This variant keeps the old error message for -std=f2003:

Index: gcc/fortran/decl.c
===================================================================
--- gcc/fortran/decl.c  (revision 169519)
+++ gcc/fortran/decl.c  (working copy)
@@ -1515,14 +1515,20 @@ build_struct (const char *name, gfc_charlen *cl, g
   gfc_component *c;
   gfc_try t = SUCCESS;
 
-  /* F03:C438/C439. If the current symbol is of the same derived type that we're
-     constructing, it must have the pointer attribute.  */
+  /* F03:C438/C439, F08:C440. If the current symbol is of the same derived type
+     that we're constructing, it must be POINTER or ALLOCATABLE.  */
   if ((current_ts.type == BT_DERIVED || current_ts.type == BT_CLASS)
       && current_ts.u.derived == gfc_current_block ()
       && current_attr.pointer == 0)
     {
-      gfc_error ("Component at %C must have the POINTER attribute");
-      return FAILURE;
+      if (gfc_notify_std (GFC_STD_F2008, "Component at %C must have the "
+                         "POINTER attribute") == FAILURE)
+       return FAILURE;
+      else if (current_attr.allocatable == 0)
+       {
+         gfc_error ("Component at %C must be POINTER or ALLOCATABLE");
+         return FAILURE;
+       }
     }
 
   if (gfc_current_block ()->attr.pointer && (*as)->rank != 0)
Comment 4 janus 2011-02-06 12:28:42 UTC
We also need to adjust gfc_get_derived_type, to avoid running into an infinite loop. Updated patch:


Index: gcc/testsuite/gfortran.dg/class_2.f03
===================================================================
--- gcc/testsuite/gfortran.dg/class_2.f03	(revision 169853)
+++ gcc/testsuite/gfortran.dg/class_2.f03	(working copy)
@@ -36,7 +36,7 @@ end interface
 
 type t6
   integer :: i
-  class(t6), allocatable :: foo  ! { dg-error "must have the POINTER attribute" }
+  class(t6) :: foo  ! { dg-error "must be POINTER or ALLOCATABLE" }
 end type t6
 
 
Index: gcc/fortran/decl.c
===================================================================
--- gcc/fortran/decl.c	(revision 169853)
+++ gcc/fortran/decl.c	(working copy)
@@ -1515,14 +1515,20 @@ build_struct (const char *name, gfc_charlen *cl, g
   gfc_component *c;
   gfc_try t = SUCCESS;
 
-  /* F03:C438/C439. If the current symbol is of the same derived type that we're
-     constructing, it must have the pointer attribute.  */
+  /* F03:C438/C439, F08:C440. If the current symbol is of the same derived type
+     that we're constructing, it must be POINTER or ALLOCATABLE.  */
   if ((current_ts.type == BT_DERIVED || current_ts.type == BT_CLASS)
       && current_ts.u.derived == gfc_current_block ()
       && current_attr.pointer == 0)
     {
-      gfc_error ("Component at %C must have the POINTER attribute");
-      return FAILURE;
+      if (gfc_notify_std (GFC_STD_F2008, "Component at %C must have the "
+			  "POINTER attribute") == FAILURE)
+	return FAILURE;
+      else if (current_attr.allocatable == 0)
+	{
+	  gfc_error ("Component at %C must be POINTER or ALLOCATABLE");
+	  return FAILURE;
+	}
     }
 
   if (gfc_current_block ()->attr.pointer && (*as)->rank != 0)
Index: gcc/fortran/trans-types.c
===================================================================
--- gcc/fortran/trans-types.c	(revision 169853)
+++ gcc/fortran/trans-types.c	(working copy)
@@ -2096,14 +2096,14 @@ gfc_get_derived_type (gfc_symbol * derived)
   /* Go through the derived type components, building them as
      necessary. The reason for doing this now is that it is
      possible to recurse back to this derived type through a
-     pointer component (PR24092). If this happens, the fields
+     pointer or allocatable component. If this happens, the fields
      will be built and so we can return the type.  */
   for (c = derived->components; c; c = c->next)
     {
       if (c->ts.type != BT_DERIVED && c->ts.type != BT_CLASS)
 	continue;
 
-      if ((!c->attr.pointer && !c->attr.proc_pointer)
+      if ((!c->attr.pointer && !c->attr.proc_pointer && !c->attr.allocatable)
 	  || c->ts.u.derived->backend_decl == NULL)
 	c->ts.u.derived->backend_decl = gfc_get_derived_type (c->ts.u.derived);
Comment 5 janus 2011-02-06 12:36:40 UTC
With the patch in comment #4, we currently get an ICE on the following test case:


type entry1
  real :: value
  type(entry1), allocatable :: next
end type entry1

type(entry1), allocatable :: x

allocate(x)

end


The problem is that we run into an infinite loop when trying to auto-deallocate x and the whole chain of its components, cf. gfc_deallocate_scalar_with_status, gfc_deallocate_alloc_comp and structure_alloc_comps.
Comment 6 Tobias Burnus 2012-08-29 09:18:01 UTC
Test case by Wolfgang Kilian in the "Function questions?" thread:
https://groups.google.com/d/msg/comp.lang.fortran/r4PVbtaBnFM/hufoSWKHDBIJ

When handling the deallocation, we need to ensure that also the FINAL wrapper properly handles it.
Comment 7 Paul Thomas 2016-10-23 09:03:00 UTC
Created attachment 39871 [details]
Patch, to be submitted, for this PR.

2016-10-23  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/45516
	* class.c (gfc_find_derived_vtab): Detect recursive allocatable
	derived type components. If present, add '_deallocate' field to
	the vtable and build the '__deallocate' function.
	* decl.c (build_struct): Allow recursive allocatable derived
	type components for -std=f2008 or more.
	(gfc_match_data_decl): Accept these derived types.
	* expr.c (gfc_has_default_initializer): Ditto.
	* resolve.c (resolve_component): Make sure that the vtable is
	built for these derived types.
	* trans-array.c(structure_alloc_comps) : Use the '__deallocate'
	function for the automatic deallocation of these types.
	* trans-expr.c : Generate the deallocate accessor.
	* trans.h : Add its prototype.
	* trans-types.c (gfc_get_derived_type): Treat the recursive
	allocatable components in the same way as the corresponding
	pointer components.

2016-10-23  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/45516
	* gfortran.dg/class_2.f03: Set -std=f2003.
	* gfortran.dg/finalize_21.f90: Modify tree-dump.
	* gfortran.dg/recursive_alloc_comp_1.f08: New test.
	* gfortran.dg/recursive_alloc_comp_2.f08: New test.
	* gfortran.dg/recursive_alloc_comp_3.f08: New test.
	* gfortran.dg/recursive_alloc_comp_4.f08: New test.
Comment 8 Paul Thomas 2016-10-25 20:37:37 UTC
Author: pault
Date: Tue Oct 25 20:37:05 2016
New Revision: 241539

URL: https://gcc.gnu.org/viewcvs?rev=241539&root=gcc&view=rev
Log:
2016-10-25  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/45516
	* class.c (gfc_find_derived_vtab): Detect recursive allocatable
	derived type components. If present, add '_deallocate' field to
	the vtable and build the '__deallocate' function.
	* decl.c (build_struct): Allow recursive allocatable derived
	type components for -std=f2008 or more.
	(gfc_match_data_decl): Accept these derived types.
	* expr.c (gfc_has_default_initializer): Ditto.
	* resolve.c (resolve_component): Make sure that the vtable is
	built for these derived types.
	* trans-array.c(structure_alloc_comps) : Use the '__deallocate'
	function for the automatic deallocation of these types.
	* trans-expr.c : Generate the deallocate accessor.
	* trans.h : Add its prototype.
	* trans-types.c (gfc_get_derived_type): Treat the recursive
	allocatable components in the same way as the corresponding
	pointer components.

2016-10-25  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/45516
	* gfortran.dg/class_2.f03: Set -std=f2003.
	* gfortran.dg/finalize_21.f90: Modify tree-dump.
	* gfortran.dg/recursive_alloc_comp_1.f08: New test.
	* gfortran.dg/recursive_alloc_comp_2.f08: New test.
	* gfortran.dg/recursive_alloc_comp_3.f08: New test.
	* gfortran.dg/recursive_alloc_comp_4.f08: New test.

Added:
    trunk/gcc/testsuite/gfortran.dg/recursive_alloc_comp_1.f08
    trunk/gcc/testsuite/gfortran.dg/recursive_alloc_comp_2.f08
    trunk/gcc/testsuite/gfortran.dg/recursive_alloc_comp_3.f08
    trunk/gcc/testsuite/gfortran.dg/recursive_alloc_comp_4.f08
Modified:
    trunk/gcc/fortran/class.c
    trunk/gcc/fortran/decl.c
    trunk/gcc/fortran/expr.c
    trunk/gcc/fortran/resolve.c
    trunk/gcc/fortran/trans-array.c
    trunk/gcc/fortran/trans-expr.c
    trunk/gcc/fortran/trans-types.c
    trunk/gcc/fortran/trans.h
    trunk/gcc/testsuite/gfortran.dg/class_2.f03
    trunk/gcc/testsuite/gfortran.dg/finalize_21.f90
Comment 9 janus 2016-11-12 15:53:07 UTC
Paul, can we close this PR, or is there anything left to do?
Comment 10 janus 2016-12-04 17:06:45 UTC
(In reply to janus from comment #9)
> Paul, can we close this PR, or is there anything left to do?

ping!
Comment 11 Paul Thomas 2016-12-04 20:58:19 UTC
I had thought about applying it to 6-branch but leaving it for a decent period has made it too late :-(

Closing the PR

Paul
Comment 12 GCC Commits 2020-10-21 12:39:07 UTC
The master branch has been updated by Tobias Burnus <burnus@gcc.gnu.org>:

https://gcc.gnu.org/g:310fe80babe04ccb7d2e15c8fca7dc98180701a8

commit r11-4186-g310fe80babe04ccb7d2e15c8fca7dc98180701a8
Author: Tobias Burnus <tobias@codesourcery.com>
Date:   Wed Oct 21 14:38:44 2020 +0200

    Fortran: class.c - update vtable comment
    
    gcc/fortran/
            PR fortran/45516
            * class.c: Add _deallocate to the vtable documentation
            comment.