Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Hi Mikael, Am 08.07.23 um 14:07 schrieb Mikael Morin: here is what I'm finally coming to. This patch fixes my example, but is otherwise untested. The patch has grown enough that I'm tempted to fix my example separately, in its own commit. alright. I've interpreted this as a green light for v2 of my patch and pushed it as r14-2395-gb1079fc88f082d https://gcc.gnu.org/g:b1079fc88f082d3c5b583c8822c08c5647810259 so that you can build upon it. Mikael Thanks, Harald
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Hello, Le 07/07/2023 à 20:23, Harald Anlauf a écrit : Hi Mikael, Am 07.07.23 um 14:21 schrieb Mikael Morin: I'm attaching what I have (lightly) tested so far, which doesn't work. It seems gfc_conv_class_to_class reevaluates part of the original expression, which is not correct after deallocation. this looks much more elegant than my attempt that passed an additional argument to gfc_conv_class_to_class, to achieve what your patch does. Will have a look again tonight. Great. Harald here is what I'm finally coming to. This patch fixes my example, but is otherwise untested. The patch has grown enough that I'm tempted to fix my example separately, in its own commit. Mikaeldiff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc index e7c51bae052..1c2af55d436 100644 --- a/gcc/fortran/trans-array.cc +++ b/gcc/fortran/trans-array.cc @@ -3271,6 +3271,7 @@ gfc_conv_ss_descriptor (stmtblock_t * block, gfc_ss * ss, int base) gfc_add_block_to_block (block, ); info->descriptor = se.expr; ss_info->string_length = se.string_length; + ss_info->class_container = se.class_container; if (base) { @@ -7687,6 +7688,8 @@ gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr) else if (deferred_array_component) se->string_length = ss_info->string_length; + se->class_container = ss_info->class_container; + gfc_free_ss_chain (ss); return; } diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index ebef1a36577..01386bceaeb 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -529,24 +529,10 @@ gfc_find_and_cut_at_last_class_ref (gfc_expr *e, bool is_mold, } -/* Reset the vptr to the declared type, e.g. after deallocation. */ - -void -gfc_reset_vptr (stmtblock_t *block, gfc_expr *e) +static void +reset_vptr (stmtblock_t *block, gfc_expr *e, tree class_expr) { - gfc_symbol *vtab; - tree vptr; - tree vtable; - gfc_se se; - - /* Evaluate the expression and obtain the vptr from it. */ - gfc_init_se (, NULL); - if (e->rank) -gfc_conv_expr_descriptor (, e); - else -gfc_conv_expr (, e); - gfc_add_block_to_block (block, ); - vptr = gfc_get_vptr_from_expr (se.expr); + tree vptr = gfc_get_vptr_from_expr (class_expr); /* If a vptr is not found, we can do nothing more. */ if (vptr == NULL_TREE) @@ -556,6 +542,9 @@ gfc_reset_vptr (stmtblock_t *block, gfc_expr *e) gfc_add_modify (block, vptr, build_int_cst (TREE_TYPE (vptr), 0)); else { + gfc_symbol *vtab; + tree vtable; + /* Return the vptr to the address of the declared type. */ vtab = gfc_find_derived_vtab (e->ts.u.derived); vtable = vtab->backend_decl; @@ -568,6 +557,24 @@ gfc_reset_vptr (stmtblock_t *block, gfc_expr *e) } +/* Reset the vptr to the declared type, e.g. after deallocation. */ + +void +gfc_reset_vptr (stmtblock_t *block, gfc_expr *e) +{ + gfc_se se; + + /* Evaluate the expression and obtain the vptr from it. */ + gfc_init_se (, NULL); + if (e->rank) +gfc_conv_expr_descriptor (, e); + else +gfc_conv_expr (, e); + gfc_add_block_to_block (block, ); + reset_vptr (block, e, se.expr); +} + + /* Reset the len for unlimited polymorphic objects. */ void @@ -1266,6 +1273,8 @@ gfc_conv_class_to_class (gfc_se *parmse, gfc_expr *e, gfc_typespec class_ts, slen = build_zero_cst (size_type_node); } + else if (parmse->class_container != NULL_TREE) +tmp = parmse->class_container; else { /* Remove everything after the last class reference, convert the @@ -3078,6 +3087,11 @@ gfc_conv_variable (gfc_se * se, gfc_expr * expr) return; } + if (sym->ts.type == BT_CLASS + && sym->attr.class_ok + && sym->ts.u.derived->attr.is_class) + se->class_container = se->expr; + /* Dereference the expression, where needed. */ se->expr = gfc_maybe_dereference_var (sym, se->expr, se->descriptor_only, is_classarray); @@ -3135,6 +3149,15 @@ gfc_conv_variable (gfc_se * se, gfc_expr * expr) conv_parent_component_references (se, ref); gfc_conv_component_ref (se, ref); + + if (ref->u.c.component->ts.type == BT_CLASS + && ref->u.c.component->attr.class_ok + && ref->u.c.component->ts.u.derived->attr.is_class) + se->class_container = se->expr; + else if (!(ref->u.c.sym->attr.flavor == FL_DERIVED + && ref->u.c.sym->attr.is_class)) + se->class_container = NULL_TREE; + if (!ref->next && ref->u.c.sym->attr.codimension && se->want_pointer && se->descriptor_only) return; @@ -6784,6 +6807,21 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, stmtblock_t block; tree ptr; + /* In case the data reference to deallocate is dependent on + its own content, save the resulting pointer to a variable + and only use that variable from now on, before the + expression becomes invalid. */ + tree t = gfc_build_addr_expr (NULL_TREE, parmse.expr); + t =
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Hi Mikael, Am 07.07.23 um 14:21 schrieb Mikael Morin: I'm attaching what I have (lightly) tested so far, which doesn't work. It seems gfc_conv_class_to_class reevaluates part of the original expression, which is not correct after deallocation. this looks much more elegant than my attempt that passed an additional argument to gfc_conv_class_to_class, to achieve what your patch does. Will have a look again tonight. Great. Harald
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Le 05/07/2023 à 22:36, Harald Anlauf a écrit : Hi Mikael, Am 05.07.23 um 16:54 schrieb Mikael Morin: Here is an example, admittedly artificial. Fails with the above change, but fails with master as well. program p implicit none type t integer :: i end type t type u class(t), allocatable :: ta(:) end type u type(u), allocatable, target :: c(:) c = [u([t(1), t(3)]), u([t(4), t(9)])] call bar (allocated (c(c(1)%ta(1)%i)%ta), c(c(1)%ta(1)%i)%ta, allocated (c(c(1)%ta(1)%i)%ta)) if (allocated(c(1)%ta)) stop 11 if (.not. allocated(c(2)%ta)) stop 12 contains subroutine bar (alloc, x, alloc2) logical :: alloc, alloc2 class(t), allocatable, intent(out) :: x(:) if (allocated (x)) stop 1 if (.not. alloc) stop 2 if (.not. alloc2) stop 3 end subroutine bar end while it looks artificial, it is valid, and IMHO it is a beast... I've played around and added another argument gfc_se *convse to gfc_conv_class_to_class in an attempt to implement what I thought you suggested (to get the .pre/.post separately), but in the end this did not lead to working code. And the tree-dump for your example above is beyond what I can grasp. I've noticed that my attempt does not properly handle the parmse.post; at least this is what the above example shows: there is a small part after the call to bar that should have been executed before that call, which I attribute to .post. But my attempts in moving that part regresses on a couple of testcases with class and intent(out). I am at a loss now. All that I can see after the call is a reassignment of the original data and vptr pointers from the temporary class container. They seem at their right place there. But part of the expression seems to be evaluated again, instead of being picked up from parmse.expr. I am attaching the latest version of my patch to give you or Paul or others the opportunity to see what is wrong or add the missing pieces. I'm attaching what I have (lightly) tested so far, which doesn't work. It seems gfc_conv_class_to_class reevaluates part of the original expression, which is not correct after deallocation. Will have a look again tonight. Mikael diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index ebef1a36577..54249c9c615 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -6819,9 +6819,13 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, defer_to_dealloc_blk = true; } + gfc_se class_se = parmse; + gfc_init_block (_se.pre); + gfc_init_block (_se.post); + /* The conversion does not repackage the reference to a class array - _data descriptor. */ - gfc_conv_class_to_class (, e, fsym->ts, false, + gfc_conv_class_to_class (_se, e, fsym->ts, false, fsym->attr.intent != INTENT_IN && (CLASS_DATA (fsym)->attr.class_pointer || CLASS_DATA (fsym)->attr.allocatable), @@ -6831,9 +6835,10 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, CLASS_DATA (fsym)->attr.class_pointer || CLASS_DATA (fsym)->attr.allocatable); - /* Defer repackaging after deallocation. */ - if (defer_to_dealloc_blk) - gfc_add_block_to_block (_blk, ); + parmse.expr = class_se.expr; + stmtblock_t *class_pre_block = defer_to_dealloc_blk ? _blk : + gfc_add_block_to_block (class_pre_block, _se.pre); + gfc_add_block_to_block (, _se.post); } else {
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Hi Mikael, Am 05.07.23 um 16:54 schrieb Mikael Morin: Here is an example, admittedly artificial. Fails with the above change, but fails with master as well. program p implicit none type t integer :: i end type t type u class(t), allocatable :: ta(:) end type u type(u), allocatable, target :: c(:) c = [u([t(1), t(3)]), u([t(4), t(9)])] call bar (allocated (c(c(1)%ta(1)%i)%ta), c(c(1)%ta(1)%i)%ta, allocated (c(c(1)%ta(1)%i)%ta)) if (allocated(c(1)%ta)) stop 11 if (.not. allocated(c(2)%ta)) stop 12 contains subroutine bar (alloc, x, alloc2) logical :: alloc, alloc2 class(t), allocatable, intent(out) :: x(:) if (allocated (x)) stop 1 if (.not. alloc) stop 2 if (.not. alloc2) stop 3 end subroutine bar end while it looks artificial, it is valid, and IMHO it is a beast... I've played around and added another argument gfc_se *convse to gfc_conv_class_to_class in an attempt to implement what I thought you suggested (to get the .pre/.post separately), but in the end this did not lead to working code. And the tree-dump for your example above is beyond what I can grasp. I've noticed that my attempt does not properly handle the parmse.post; at least this is what the above example shows: there is a small part after the call to bar that should have been executed before that call, which I attribute to .post. But my attempts in moving that part regresses on a couple of testcases with class and intent(out). I am at a loss now. I am attaching the latest version of my patch to give you or Paul or others the opportunity to see what is wrong or add the missing pieces. Thanks for your help so far. Harald From 989030fc04eacf97a034ab1f7ed85b932669f82d Mon Sep 17 00:00:00 2001 From: Harald Anlauf Date: Wed, 5 Jul 2023 22:21:09 +0200 Subject: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178] gcc/fortran/ChangeLog: PR fortran/92178 * trans-expr.cc (gfc_conv_procedure_call): Check procedures for allocatable dummy arguments with INTENT(OUT) and move deallocation of actual arguments after evaluation of argument expressions before the procedure is executed. gcc/testsuite/ChangeLog: PR fortran/92178 * gfortran.dg/intent_out_16.f90: New test. * gfortran.dg/intent_out_17.f90: New test. * gfortran.dg/intent_out_18.f90: New test. Co-authored-by: Steven G. Kargl --- gcc/fortran/trans-expr.cc | 54 +++-- gcc/testsuite/gfortran.dg/intent_out_16.f90 | 89 + gcc/testsuite/gfortran.dg/intent_out_17.f90 | 46 +++ gcc/testsuite/gfortran.dg/intent_out_18.f90 | 31 +++ 4 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/intent_out_16.f90 create mode 100644 gcc/testsuite/gfortran.dg/intent_out_17.f90 create mode 100644 gcc/testsuite/gfortran.dg/intent_out_18.f90 diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index 30946ba3f63..7017b652d6e 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -6085,9 +6085,10 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else info = NULL; - stmtblock_t post, clobbers; + stmtblock_t post, clobbers, dealloc_blk; gfc_init_block (); gfc_init_block (); + gfc_init_block (_blk); gfc_init_interface_mapping (); if (!comp) { @@ -6117,6 +6118,32 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, && UNLIMITED_POLY (sym) && comp && (strcmp ("_copy", comp->name) == 0); + /* Scan for allocatable actual arguments passed to allocatable dummy + arguments with INTENT(OUT). As the corresponding actual arguments are + deallocated before execution of the procedure, we evaluate actual + argument expressions to avoid problems with possible dependencies. */ + bool force_eval_args = false; + gfc_formal_arglist *tmp_formal; + for (arg = args, tmp_formal = formal; arg != NULL; + arg = arg->next, tmp_formal = tmp_formal ? tmp_formal->next : NULL) +{ + e = arg->expr; + fsym = tmp_formal ? tmp_formal->sym : NULL; + if (e && fsym + && e->expr_type == EXPR_VARIABLE + && fsym->attr.intent == INTENT_OUT + && (fsym->ts.type == BT_CLASS && fsym->attr.class_ok + ? CLASS_DATA (fsym)->attr.allocatable + : fsym->attr.allocatable) + && e->symtree + && e->symtree->n.sym + && gfc_variable_attr (e, NULL).allocatable) + { + force_eval_args = true; + break; + } +} + /* Evaluate the arguments. */ for (arg = args, argc = 0; arg != NULL; arg = arg->next, formal = formal ? formal->next : NULL, ++argc) @@ -6680,7 +6707,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else tmp = gfc_finish_block (); - gfc_add_expr_to_block (>pre, tmp); + gf
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Le 04/07/2023 à 21:37, Mikael Morin a écrit : Le 04/07/2023 à 21:00, Harald Anlauf a écrit : 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 think that's it mostly. There is one last thing that I am not sure... 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 @@ -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 (_blk, ); } else { ... whether you will not be deferring too much here. That is parmse.pre contains both the argument evaluation and the class container setup from gfc_conv_class_to_class. If it's safe to defer both, that's fine, otherwise a separate gfc_se struct should be passed to gfc_conv_class_to_class so that only the latter part can be deferred. Need to think of an example... Here is an example, admittedly artificial. Fails with the above change, but fails with master as well. program p implicit none type t integer :: i end type t type u class(t), allocatable :: ta(:) end type u type(u), allocatable, target :: c(:) c = [u([t(1), t(3)]), u([t(4), t(9)])] call bar (allocated (c(c(1)%ta(1)%i)%ta), c(c(1)%ta(1)%i)%ta, allocated (c(c(1)%ta(1)%i)%ta)) if (allocated(c(1)%ta)) stop 11 if (.not. allocated(c(2)%ta)) stop 12 contains subroutine bar (alloc, x, alloc2) logical :: alloc, alloc2 class(t), allocatable, intent(out) :: x(:) if (allocated (x)) stop 1 if (.not. alloc) stop 2 if (.not. alloc2) stop 3 end subroutine bar end
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Le 04/07/2023 à 21:00, Harald Anlauf a écrit : 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 think that's it mostly. There is one last thing that I am not sure... 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 @@ -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 (_blk, ); } else { ... whether you will not be deferring too much here. That is parmse.pre contains both the argument evaluation and the class container setup from gfc_conv_class_to_class. If it's safe to defer both, that's fine, otherwise a separate gfc_se struct should be passed to gfc_conv_class_to_class so that only the latter part can be deferred. Need to think of an example...
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
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 (); - gfc_add_expr_to_block (>pre, tmp); +// gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_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 (, , ); 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 (, , ); 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 (, 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 (); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_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 (_blk, ); } 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
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
On Tue, Jul 04, 2023 at 11:26:26AM +0200, Mikael Morin wrote: > Le 04/07/2023 à 01:56, Steve Kargl a écrit : > > On Mon, Jul 03, 2023 at 10:49:36PM +0200, Harald Anlauf via Fortran wrote: > > > > > > Indeed, this is a nice demonstration. > > > > > > While playing, I was wondering whether the following code is conforming: > > > > > > program p > > >call s ((1)) > > > contains > > >subroutine s (x) > > > integer :: x > > > x = 42 > > >end subroutine > > > end > > > > > > (It crashes with gfortran, but not with any foreign brand tested). > > > > > > > It's not conforming. '(1)' is an expression and it cannot appear > > in a variable definition condition. I am not aware of any numbered > > constraint tha would require a Fortran processor to generate an > > error. > > > > I think you would be right if X had INTENT(OUT) or INTENT(INOUT) attribute. > This is F2023, 15.5.2.4 (no mention of variable definition context here): > > If a dummy argument has INTENT (OUT) or INTENT (INOUT), the actual > > argument shall be definable. > > However, with unspecified intent, I can't find the rule explicitly > forbidding the above example. > I'm tempted to say it is conforming. I thought it was in Sec. 19, but failed to locate any prohibition. The best I can find is 23-007r1.pdf 8.5.10 INTENT attribute pg. 114 (following Note 1) If no INTENT attribute is specified for a dummy argument, its use is subject to the limitations of its effective argument (15.5.2). pg. 115 (within Note 4, so non-normative text) INTENT (INOUT) is not equivalent to omitting the INTENT attribute. The actual argument corresponding to an INTENT (INOUT) dummy argument is always required to be definable, while an actual argument corresponding to a dummy argument without an INTENT attribute need be definable only if the dummy argument is actually redefined. Searching for "definable" does not lead to a prohibition of the form "An expression is not definable." -- Steve
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
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 (); - gfc_add_expr_to_block (>pre, tmp); +// gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_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 (, , ); 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 (, , ); 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
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Le 04/07/2023 à 01:56, Steve Kargl a écrit : On Mon, Jul 03, 2023 at 10:49:36PM +0200, Harald Anlauf via Fortran wrote: Indeed, this is a nice demonstration. While playing, I was wondering whether the following code is conforming: program p call s ((1)) contains subroutine s (x) integer :: x x = 42 end subroutine end (It crashes with gfortran, but not with any foreign brand tested). It's not conforming. '(1)' is an expression and it cannot appear in a variable definition condition. I am not aware of any numbered constraint tha would require a Fortran processor to generate an error. I think you would be right if X had INTENT(OUT) or INTENT(INOUT) attribute. This is F2023, 15.5.2.4 (no mention of variable definition context here): If a dummy argument has INTENT (OUT) or INTENT (INOUT), the actual argument shall be definable. However, with unspecified intent, I can't find the rule explicitly forbidding the above example. I'm tempted to say it is conforming.
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
On Mon, Jul 03, 2023 at 10:49:36PM +0200, Harald Anlauf via Fortran wrote: > > Indeed, this is a nice demonstration. > > While playing, I was wondering whether the following code is conforming: > > program p > call s ((1)) > contains > subroutine s (x) > integer :: x > x = 42 > end subroutine > end > > (It crashes with gfortran, but not with any foreign brand tested). > It's not conforming. '(1)' is an expression and it cannot appear in a variable definition condition. I am not aware of any numbered constraint tha would require a Fortran processor to generate an error. -- Steve
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Hi Mikael, Am 03.07.23 um 13:46 schrieb Mikael Morin: A few thing to double check below. diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index 30946ba3f63..16e8f037cfc 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc (...) @@ -6117,6 +6118,33 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, && UNLIMITED_POLY (sym) && comp && (strcmp ("_copy", comp->name) == 0); + /* First scan argument list for allocatable actual arguments passed to + allocatable dummy arguments with INTENT(OUT). As the corresponding + actual arguments are deallocated before execution of the procedure, we + evaluate actual argument expressions to avoid problems with possible + dependencies. */ + bool force_eval_args = false; + gfc_formal_arglist *tmp_formal; + for (arg = args, tmp_formal = formal; arg != NULL; + arg = arg->next, tmp_formal = tmp_formal ? tmp_formal->next : NULL) + { + e = arg->expr; + fsym = tmp_formal ? tmp_formal->sym : NULL; + if (e && fsym + && e->expr_type == EXPR_VARIABLE + && fsym->attr.intent == INTENT_OUT + && (fsym->ts.type == BT_CLASS && fsym->attr.class_ok + ? CLASS_DATA (fsym)->attr.allocatable + : fsym->attr.allocatable) + && e->symtree + && e->symtree->n.sym + && gfc_variable_attr (e, NULL).allocatable) + { + force_eval_args = true; + break; + } + } + The function is already big enough, would you mind outlining this to its own function? This can be done. At least it is not part of the monster loop. /* Evaluate the arguments. */ for (arg = args, argc = 0; arg != NULL; arg = arg->next, formal = formal ? formal->next : NULL, ++argc) @@ -6680,7 +6708,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else tmp = gfc_finish_block (); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } /* A class array element needs converting back to be a @@ -6980,7 +7008,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, build_empty_stmt (input_location)); } if (tmp != NULL_TREE) - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } tmp = parmse.expr; @@ -7004,7 +7032,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, void_type_node, gfc_conv_expr_present (e->symtree->n.sym), tmp, build_empty_stmt (input_location)); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } } } 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 (); - gfc_add_expr_to_block (>pre, tmp); +// gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_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...) @@ -7101,6 +7129,21 @@ 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 + 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
Re: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Hello, Le 02/07/2023 à 22:38, Harald Anlauf via Fortran a écrit : Dear all, the attached patch fixes a long-standing issue with the order of evaluation of procedure argument expressions and deallocation of allocatable actual arguments passed to allocatable dummies with intent(out) attribute. It is based on an initial patch by Steve, handles issues pointed out by Tobias, and includes a suggestion by Tobias to scan the procedure arguments first to decide whether the creation of temporaries is needed. There is one unresolved issue left that might be more general: it appears to affect character arguments (only) in that quite often there still is no temporary generated. I haven't found the reason why and would like to defer this, unless someone has a good suggestion. No problem, let's fix the easier parts first. Regtested on x86_64-pc-linux-gnu. OK for mainline? A few thing to double check below. pr92178.diff From 609ba636927811cddc74fb815cb18809c7d33565 Mon Sep 17 00:00:00 2001 From: Harald Anlauf Date: Sun, 2 Jul 2023 22:14:19 +0200 Subject: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178] gcc/fortran/ChangeLog: PR fortran/92178 * trans-expr.cc (gfc_conv_procedure_call): Check procedures for allocatable dummy arguments with INTENT(OUT) and move deallocation of actual arguments after evaluation of argument expressions before the procedure is executed. gcc/testsuite/ChangeLog: PR fortran/92178 * gfortran.dg/pr92178.f90: New test. * gfortran.dg/pr92178_2.f90: New test. Co-authored-by: Steven G. Kargl --- gcc/fortran/trans-expr.cc | 52 ++-- gcc/testsuite/gfortran.dg/pr92178.f90 | 83 + gcc/testsuite/gfortran.dg/pr92178_2.f90 | 46 ++ 3 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/pr92178.f90 create mode 100644 gcc/testsuite/gfortran.dg/pr92178_2.f90 diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index 30946ba3f63..16e8f037cfc 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc (...) @@ -6117,6 +6118,33 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, && UNLIMITED_POLY (sym) && comp && (strcmp ("_copy", comp->name) == 0); + /* First scan argument list for allocatable actual arguments passed to + allocatable dummy arguments with INTENT(OUT). As the corresponding + actual arguments are deallocated before execution of the procedure, we + evaluate actual argument expressions to avoid problems with possible + dependencies. */ + bool force_eval_args = false; + gfc_formal_arglist *tmp_formal; + for (arg = args, tmp_formal = formal; arg != NULL; + arg = arg->next, tmp_formal = tmp_formal ? tmp_formal->next : NULL) +{ + e = arg->expr; + fsym = tmp_formal ? tmp_formal->sym : NULL; + if (e && fsym + && e->expr_type == EXPR_VARIABLE + && fsym->attr.intent == INTENT_OUT + && (fsym->ts.type == BT_CLASS && fsym->attr.class_ok + ? CLASS_DATA (fsym)->attr.allocatable + : fsym->attr.allocatable) + && e->symtree + && e->symtree->n.sym + && gfc_variable_attr (e, NULL).allocatable) + { + force_eval_args = true; + break; + } +} + The function is already big enough, would you mind outlining this to its own function? /* Evaluate the arguments. */ for (arg = args, argc = 0; arg != NULL; arg = arg->next, formal = formal ? formal->next : NULL, ++argc) @@ -6680,7 +6708,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else tmp = gfc_finish_block (); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } /* A class array element needs converting back to be a @@ -6980,7 +7008,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, build_empty_stmt (input_location)); } if (tmp != NULL_TREE) - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } tmp = parmse.expr; @@ -7004,7 +7032,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, void_type_node, gfc_conv_expr_present (e->symtree->n.sym), tmp, build_empty_stmt (input_location)); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp);
[PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178]
Dear all, the attached patch fixes a long-standing issue with the order of evaluation of procedure argument expressions and deallocation of allocatable actual arguments passed to allocatable dummies with intent(out) attribute. It is based on an initial patch by Steve, handles issues pointed out by Tobias, and includes a suggestion by Tobias to scan the procedure arguments first to decide whether the creation of temporaries is needed. There is one unresolved issue left that might be more general: it appears to affect character arguments (only) in that quite often there still is no temporary generated. I haven't found the reason why and would like to defer this, unless someone has a good suggestion. Regtested on x86_64-pc-linux-gnu. OK for mainline? Thanks, Harald From 609ba636927811cddc74fb815cb18809c7d33565 Mon Sep 17 00:00:00 2001 From: Harald Anlauf Date: Sun, 2 Jul 2023 22:14:19 +0200 Subject: [PATCH] Fortran: fixes for procedures with ALLOCATABLE,INTENT(OUT) arguments [PR92178] gcc/fortran/ChangeLog: PR fortran/92178 * trans-expr.cc (gfc_conv_procedure_call): Check procedures for allocatable dummy arguments with INTENT(OUT) and move deallocation of actual arguments after evaluation of argument expressions before the procedure is executed. gcc/testsuite/ChangeLog: PR fortran/92178 * gfortran.dg/pr92178.f90: New test. * gfortran.dg/pr92178_2.f90: New test. Co-authored-by: Steven G. Kargl --- gcc/fortran/trans-expr.cc | 52 ++-- gcc/testsuite/gfortran.dg/pr92178.f90 | 83 + gcc/testsuite/gfortran.dg/pr92178_2.f90 | 46 ++ 3 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/pr92178.f90 create mode 100644 gcc/testsuite/gfortran.dg/pr92178_2.f90 diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index 30946ba3f63..16e8f037cfc 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -6085,9 +6085,10 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else info = NULL; - stmtblock_t post, clobbers; + stmtblock_t post, clobbers, dealloc_blk; gfc_init_block (); gfc_init_block (); + gfc_init_block (_blk); gfc_init_interface_mapping (); if (!comp) { @@ -6117,6 +6118,33 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, && UNLIMITED_POLY (sym) && comp && (strcmp ("_copy", comp->name) == 0); + /* First scan argument list for allocatable actual arguments passed to + allocatable dummy arguments with INTENT(OUT). As the corresponding + actual arguments are deallocated before execution of the procedure, we + evaluate actual argument expressions to avoid problems with possible + dependencies. */ + bool force_eval_args = false; + gfc_formal_arglist *tmp_formal; + for (arg = args, tmp_formal = formal; arg != NULL; + arg = arg->next, tmp_formal = tmp_formal ? tmp_formal->next : NULL) +{ + e = arg->expr; + fsym = tmp_formal ? tmp_formal->sym : NULL; + if (e && fsym + && e->expr_type == EXPR_VARIABLE + && fsym->attr.intent == INTENT_OUT + && (fsym->ts.type == BT_CLASS && fsym->attr.class_ok + ? CLASS_DATA (fsym)->attr.allocatable + : fsym->attr.allocatable) + && e->symtree + && e->symtree->n.sym + && gfc_variable_attr (e, NULL).allocatable) + { + force_eval_args = true; + break; + } +} + /* Evaluate the arguments. */ for (arg = args, argc = 0; arg != NULL; arg = arg->next, formal = formal ? formal->next : NULL, ++argc) @@ -6680,7 +6708,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, else tmp = gfc_finish_block (); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } /* A class array element needs converting back to be a @@ -6980,7 +7008,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, build_empty_stmt (input_location)); } if (tmp != NULL_TREE) - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } tmp = parmse.expr; @@ -7004,7 +7032,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym, void_type_node, gfc_conv_expr_present (e->symtree->n.sym), tmp, build_empty_stmt (input_location)); - gfc_add_expr_to_block (>pre, tmp); + gfc_add_expr_to_block (_blk, tmp); } } } @@ -7101,6 +7129,21 @@ 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 + performed and the procedure is executed. This ensures we conform + to F