Hi Mikael, all,

I think I've found it: there is a call to gfc_conv_class_to_class
that - according to a comment - does a repackaging to a class array.
Deferring that repackaging along with the deallocation not only fixes
the regression, but also the cases I tested.

Attached is a "sneak preview", hoping that the experts (Paul, Mikael,
...) can tell if I am going down the wrong road.

I'll wrap up all pieces and resubmit when the dust settles.

We can then address the other findings later.

Harald

Am 04.07.23 um 15:35 schrieb Mikael Morin:
Le 03/07/2023 à 22:49, Harald Anlauf a écrit :
Hi Mikael,

Am 03.07.23 um 13:46 schrieb Mikael Morin:
These look good, but I'm surprised that there is no similar change at
the 6819 line.
This is the class array actual vs class array dummy case.
It seems to be checked by the "bar" subroutine in your testcase, except
that the intent(out) argument comes last there, whereas it was coming
first with the original testcases in the PR.
Can you double check?

I believe I tried that before and encountered regressions.
The change

diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 16e8f037cfc..43e013fa720 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -6844,7 +6844,8 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol *
sym,
                   else
                     tmp = gfc_finish_block (&block);

-                 gfc_add_expr_to_block (&se->pre, tmp);
+//               gfc_add_expr_to_block (&se->pre, tmp);
+                 gfc_add_expr_to_block (&dealloc_blk, tmp);
                 }

               /* The conversion does not repackage the reference to a
class

regresses on:
gfortran.dg/class_array_16.f90
gfortran.dg/finalize_12.f90
gfortran.dg/optional_class_1.f90

A simplified testcase for further study:

program p
   implicit none
   class(*),  allocatable :: c(:)
   c = [3, 4]
   call bar (allocated (c), c, allocated (c))
   if (allocated (c)) stop 14
contains
   subroutine bar (alloc, x, alloc2)
     logical :: alloc, alloc2
     class(*), allocatable, intent(out) :: x(:)
     if (allocated (x)) stop 5
     if (.not. alloc)   stop 6
     if (.not. alloc2)  stop 16
   end subroutine bar
end

(This fails in a different place for the posted patch and for
the above trial change.  Need to go to the drawing board...)

I've had a quick look.

The code originally generated looks like:

     D.4343 = (void *[0:] * restrict) c._data.data != 0B;
     if (c._data.data != 0B)
       // free c._data.data
     c._data.data = 0B;
     ...
     class.3._data = c._data;
     ...
     D.4345 = (void *[0:] * restrict) c._data.data != 0B;
     bar (&D.4343, &class.3, &D.4345);

this fails because D.4345 has the wrong value.
With your change, it becomes:

     D.4343 = (void *[0:] * restrict) c._data.data != 0B;
     ...
     class.3._data = c._data;
     ...
     D.4345 = (void *[0:] * restrict) c._data.data != 0B;
     if (c._data.data != 0B)
       // free c._data.data
     c._data.data = 0B;
     bar (&D.4343, &class.3, &D.4345);

and then it is class.3._data that has the wrong value.
So basically the initialization of class.3 should move with the
deallocation.

I can reproduce a similar problem with your unmodified patch on the
following variant:

program p
   implicit none
   class(*),  allocatable :: c
   c = 3
   call bar (c, allocated (c))
   if (allocated (c)) stop 14
contains
   subroutine bar (x, alloc2)
     logical :: alloc, alloc2
     class(*), allocatable, intent(out) :: x(..)
     if (allocated (x)) stop 5
     if (.not. alloc)   stop 6
     if (.not. alloc2)  stop 16
   end subroutine bar
end



diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 16e8f037cfc..a68c8d33acc 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -6804,6 +6804,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 	      /* Pass a class array.  */
 	      parmse.use_offset = 1;
 	      gfc_conv_expr_descriptor (&parmse, e);
+	      bool defer_repackage = false;
 
 	      /* If an ALLOCATABLE dummy argument has INTENT(OUT) and is
 		 allocated on entry, it must be deallocated.  */
@@ -6844,7 +6845,8 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 		  else
 		    tmp = gfc_finish_block (&block);
 
-		  gfc_add_expr_to_block (&se->pre, tmp);
+		  gfc_add_expr_to_block (&dealloc_blk, tmp);
+		  defer_repackage = true;
 		}
 
 	      /* The conversion does not repackage the reference to a class
@@ -6858,6 +6860,10 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 				     && e->symtree->n.sym->attr.optional,
 				     CLASS_DATA (fsym)->attr.class_pointer
 				     || CLASS_DATA (fsym)->attr.allocatable);
+
+	      /* Defer repackaging after deallocation.  */
+	      if (defer_repackage)
+		gfc_add_block_to_block (&dealloc_blk, &parmse.pre);
 	    }
 	  else
 	    {
@@ -7131,17 +7137,12 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 
       /* If any actual argument of the procedure is allocatable and passed
 	 to an allocatable dummy with INTENT(OUT), we conservatively
-	 evaluate all actual argument expressions before deallocations are
+	 evaluate actual argument expressions before deallocations are
 	 performed and the procedure is executed.  This ensures we conform
-	 to F2023:15.5.3, 15.5.4.  Create temporaries except for constants,
-	 variables, and functions returning pointers that can appear in a
-	 variable definition context.  */
+	 to F2023:15.5.3, 15.5.4.  May create temporaries when needed.  */
       if (e && fsym && force_eval_args
-	  && e->expr_type != EXPR_VARIABLE
-	  && !gfc_is_constant_expr (e)
-	  && (e->expr_type != EXPR_FUNCTION
-	      || !(gfc_expr_attr (e).pointer
-		   || gfc_expr_attr (e).proc_pointer)))
+	  && fsym->attr.intent != INTENT_OUT
+	  && !gfc_is_constant_expr (e))
 	parmse.expr = gfc_evaluate_now (parmse.expr, &parmse.pre);
 
       if (fsym && need_interface_mapping && e)

Reply via email to