On 8/13/26 7:05 AM, Mikael Morin wrote:
Le 13/08/2026 à 00:27, Jerry DeLisle a écrit :
See below, I added a version number on the patch file name, re-attached.
Err, I double checked again, still the same patch.
The second version patch was in [1], and as followup to my answer, your next
message with a patch [2] and the latest [3] contain the same patch as [1].
[1] https://gcc.gnu.org/pipermail/fortran/2026-August/064457.html
[2] https://gcc.gnu.org/pipermail/fortran/2026-August/064463.html
[3] https://gcc.gnu.org/pipermail/fortran/2026-August/064469.html
On 8/10/26 8:20 AM, Jerry D wrote:
What did go away is the particular part. There is now one check,
gfc_is_span_addressed_dummy (gfc_symbol *)
used by gfc_build_dummy_array_decl, gfc_get_symbol_decl,
This function `gfc_is_span_addressed_dummy' is nowhere in the patch. So the
message must have the wrong patch attached to it?
I made it home late yesterday. Please accept my apologies.
The attached is the correct patch file.
OK for mainline?
Best regards,
Jerry
From f47f69ef29a10a4b0218c504ffef1bc375037323 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Mon, 10 Aug 2026 09:37:01 -0700
Subject: [PATCH] fortran: [PR53800] Wrong copy-in/out with array actual to
TARGET dummy
An actual argument whose elements are spaced by more than the element
size - a CLASS array, or a component of a derived-type array - was
copied in and out when passed to a TARGET or POINTER dummy, so pointers
associated with the dummy went stale on return.
Such an actual argument is now passed with a descriptor of its own,
whose element type is that of the subobject and whose span is the
element size of the array, as is already done for a pointer assignment
to a subobject of an array. The dummy addresses its elements through
that span. Passing it on to a dummy that requires contiguous storage
copies it, but only if it turns out not to be contiguous.
PR fortran/53800
gcc/fortran/ChangeLog:
* gfortran.h (gfc_is_span_addressed_dummy): New prototype.
* symbol.cc (gfc_is_span_addressed_dummy): New function.
* expr.cc (is_subref_array): Return true for a span addressed dummy.
* trans.h (gfc_get_span_descriptor): New prototype.
* trans.cc (gfc_get_span_descriptor): New function returning the
descriptor that carries a pointer array decl's span.
(get_array_span): Use it, including for character types.
* trans-array.cc (is_pointer_array): Note in the comment that the
tree must be a descriptor.
(span_addressed_array): New function returning the decl that
provides the span of an array.
(gfc_get_array_span): Use it.
(gfc_conv_scalarized_array_ref, gfc_conv_array_ref): Likewise, so
that a descriptorless dummy is addressed by its span.
(gfc_get_dataptr_offset): Likewise and dereference a spanned
character element.
(gfc_conv_expr_descriptor): Describe a subobject of the array
elements by a new descriptor when no temporary is made.
* trans-decl.cc (gfc_build_dummy_array_decl): Use
gfc_is_span_addressed_dummy to mark the dummy as a pointer array.
(gfc_get_symbol_decl): Likewise.
* trans-expr.cc (is_subobject_ref): New function.
(copy_in_out_allowed): Take the actual argument. Use
gfc_is_span_addressed_dummy and keep the copy for an actual
argument with a vector subscript.
(gfc_conv_gfc_desc_to_cfi_desc): Remove the now duplicate offsetting
of the data pointer for a subobject reference.
(gfc_class_array_data_assign): Also copy the span field.
(gfc_conv_procedure_call): Use copy_in_out_allowed to skip
copy-in/copy-out for a class array reference, a class array function
result and a subref array. Pass a spanned descriptor instead and
make the copy of a span addressed dummy conditional on it not being
contiguous.
* trans-intrinsic.cc (gfc_conv_is_contiguous_expr): Check the span
of a span addressed dummy against the element length.
gcc/testsuite/ChangeLog:
* gfortran.dg/c_loc_test_22.f90: Update dump patterns for span
addressing.
* gfortran.dg/class_to_type_5.f90: New test.
* gfortran.dg/class_to_type_6.f90: New test.
* gfortran.dg/class_to_type_7.f90: New test.
* gfortran.dg/class_to_type_8.f90: New test.
* gfortran.dg/class_to_type_9.f90: New test.
libgomp/ChangeLog:
* testsuite/libgomp.oacc-fortran/host_data-5.F90: Update the
privatization notes for the packing of a TARGET dummy.
---
gcc/fortran/expr.cc | 3 +-
gcc/fortran/gfortran.h | 1 +
gcc/fortran/symbol.cc | 24 +++
gcc/fortran/trans-array.cc | 69 +++++---
gcc/fortran/trans-decl.cc | 22 ++-
gcc/fortran/trans-expr.cc | 92 ++++++++---
gcc/fortran/trans-intrinsic.cc | 7 +-
gcc/fortran/trans.cc | 31 +++-
gcc/fortran/trans.h | 3 +
gcc/testsuite/gfortran.dg/c_loc_test_22.f90 | 6 +-
gcc/testsuite/gfortran.dg/class_to_type_5.f90 | 35 ++++
gcc/testsuite/gfortran.dg/class_to_type_6.f90 | 93 +++++++++++
gcc/testsuite/gfortran.dg/class_to_type_7.f90 | 151 ++++++++++++++++++
gcc/testsuite/gfortran.dg/class_to_type_8.f90 | 47 ++++++
gcc/testsuite/gfortran.dg/class_to_type_9.f90 | 67 ++++++++
.../libgomp.oacc-fortran/host_data-5.F90 | 15 +-
16 files changed, 612 insertions(+), 54 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/class_to_type_5.f90
create mode 100644 gcc/testsuite/gfortran.dg/class_to_type_6.f90
create mode 100644 gcc/testsuite/gfortran.dg/class_to_type_7.f90
create mode 100644 gcc/testsuite/gfortran.dg/class_to_type_8.f90
create mode 100644 gcc/testsuite/gfortran.dg/class_to_type_9.f90
diff --git a/gcc/fortran/expr.cc b/gcc/fortran/expr.cc
index 20eddbefd57..52945a1db13 100644
--- a/gcc/fortran/expr.cc
+++ b/gcc/fortran/expr.cc
@@ -1222,7 +1222,8 @@ is_subref_array (gfc_expr * e)
sym = e->symtree->n.sym;
- if (sym->attr.subref_array_pointer)
+ if (sym->attr.subref_array_pointer
+ || gfc_is_span_addressed_dummy (sym))
return true;
seen_array = false;
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 5bd8dd50c1c..2a1f1608fba 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -3971,6 +3971,7 @@ bool gfc_check_symbol_typed (gfc_symbol*, gfc_namespace*, bool, locus);
gfc_namespace* gfc_find_proc_namespace (gfc_namespace*);
bool gfc_is_associate_pointer (gfc_symbol*);
+bool gfc_is_span_addressed_dummy (gfc_symbol *);
gfc_symbol * gfc_find_dt_in_generic (gfc_symbol *);
gfc_formal_arglist *gfc_sym_get_dummy_args (gfc_symbol *);
diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc
index 36e1262fff5..dde3e92a33d 100644
--- a/gcc/fortran/symbol.cc
+++ b/gcc/fortran/symbol.cc
@@ -5702,6 +5702,30 @@ gfc_is_associate_pointer (gfc_symbol* sym)
}
+/* Check if a dummy argument must be addressed using the span of its
+ descriptor. The actual argument of an assumed shape or assumed rank
+ TARGET dummy is never copied, so its elements can be spaced by more
+ than the element size. CLASS and assumed type entities already carry
+ their element size and are excluded. */
+
+bool
+gfc_is_span_addressed_dummy (gfc_symbol *sym)
+{
+ return sym->attr.dummy
+ && sym->attr.target
+ && sym->attr.dimension
+ && !sym->attr.value
+ && !sym->attr.contiguous
+ && !sym->attr.pointer
+ && !sym->attr.allocatable
+ && sym->ts.type != BT_CLASS
+ && sym->ts.type != BT_ASSUMED
+ && sym->as
+ && (sym->as->type == AS_ASSUMED_SHAPE
+ || sym->as->type == AS_ASSUMED_RANK);
+}
+
+
gfc_symbol *
gfc_find_dt_in_generic (gfc_symbol *sym)
{
diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index f3c9b815dd6..348039a0907 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -458,7 +458,8 @@ gfc_add_ss_to_loop (gfc_loopinfo * loop, gfc_ss * head)
}
-/* Returns true if the expression is an array pointer. */
+/* Returns true if the expression is an array pointer. The tree must be a
+ descriptor. */
static bool
is_pointer_array (tree expr)
@@ -480,7 +481,7 @@ is_pointer_array (tree expr)
&& GFC_DECL_PTR_ARRAY_P (TREE_OPERAND (expr, 0)))
return true;
- /* The field declaration is marked as an pointer array. */
+ /* The field declaration is marked as a pointer array. */
if (TREE_CODE (expr) == COMPONENT_REF
&& GFC_DECL_PTR_ARRAY_P (TREE_OPERAND (expr, 1))
&& !GFC_CLASS_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 1))))
@@ -490,6 +491,29 @@ is_pointer_array (tree expr)
}
+/* If the elements of the array are spaced by the span of its descriptor,
+ return the decl that provides that span, otherwise NULL_TREE. This is
+ either a descriptor or the local decl of a descriptorless dummy array,
+ which keeps the descriptor it was built from as the saved one. */
+
+static tree
+span_addressed_array (tree expr)
+{
+ if (is_pointer_array (expr))
+ return expr;
+
+ if (VAR_P (expr)
+ && GFC_DECL_PTR_ARRAY_P (expr)
+ && !GFC_DECL_CLASS (expr)
+ && GFC_ARRAY_TYPE_P (TREE_TYPE (expr))
+ && DECL_LANG_SPECIFIC (expr)
+ && GFC_DECL_SAVED_DESCRIPTOR (expr))
+ return expr;
+
+ return NULL_TREE;
+}
+
+
/* If the symbol or expression reference a CFI descriptor, return the
pointer to the converted gfc descriptor. If an array reference is
present as the last argument, check that it is the one applied to
@@ -554,18 +578,13 @@ gfc_get_array_span (tree desc, gfc_expr *expr)
gfc_symbol *sym = (expr && expr->expr_type == EXPR_VARIABLE) ?
expr->symtree->n.sym : NULL;
- if (is_pointer_array (desc)
+ if (span_addressed_array (desc)
|| (get_CFI_desc (NULL, expr, &desc, NULL)
&& (POINTER_TYPE_P (TREE_TYPE (desc))
? GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (TREE_TYPE (desc)))
: GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (desc)))))
- {
- if (POINTER_TYPE_P (TREE_TYPE (desc)))
- desc = build_fold_indirect_ref_loc (input_location, desc);
-
- /* This will have the span field set. */
- tmp = gfc_conv_descriptor_span_get (desc);
- }
+ /* This will have the span field set. */
+ tmp = gfc_conv_descriptor_span_get (gfc_get_span_descriptor (desc));
else if (expr->ts.type == BT_ASSUMED)
{
if (DECL_LANG_SPECIFIC (desc) && GFC_DECL_SAVED_DESCRIPTOR (desc))
@@ -3964,7 +3983,7 @@ gfc_conv_scalarized_array_ref (gfc_se * se, gfc_array_ref * ar,
/* A pointer array component can be detected from its field decl. Fix
the descriptor, mark the resulting variable decl and pass it to
gfc_build_array_ref. */
- if (is_pointer_array (info->descriptor)
+ if (span_addressed_array (info->descriptor)
|| (expr && expr->ts.deferred && info->descriptor
&& GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (info->descriptor))))
{
@@ -4217,11 +4236,9 @@ gfc_conv_array_ref (gfc_se * se, gfc_array_ref * ar, gfc_expr *expr,
if (get_CFI_desc (sym, expr, &decl, ar))
decl = build_fold_indirect_ref_loc (input_location, decl);
if (!expr->ts.deferred && !sym->attr.codimension
- && is_pointer_array (se->expr))
+ && span_addressed_array (se->expr))
{
- if (TREE_CODE (se->expr) == COMPONENT_REF)
- decl = se->expr;
- else if (INDIRECT_REF_P (se->expr))
+ if (INDIRECT_REF_P (se->expr))
decl = TREE_OPERAND (se->expr, 0);
else
decl = se->expr;
@@ -7595,7 +7612,13 @@ gfc_get_dataptr_offset (stmtblock_t *block, tree parm, tree desc, tree offset,
return;
}
- tmp = build_array_ref (desc, offset, NULL, NULL);
+ /* An array whose elements are spaced by the span needs pointer arithmetic
+ to reference an element. */
+ tmp = build_array_ref (desc, offset, span_addressed_array (desc), NULL);
+
+ /* A spanned character element is referenced by a pointer. */
+ if (POINTER_TYPE_P (TREE_TYPE (tmp)) && span_addressed_array (desc))
+ tmp = build_fold_indirect_ref_loc (input_location, tmp);
/* Offset the data pointer for pointer assignments from arrays with
subreferences; e.g. my_integer => my_type(:)%integer_component. */
@@ -8058,6 +8081,7 @@ gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr)
subref_array_target = (is_subref_array (expr)
&& (se->direct_byref
+ || se->force_no_tmp
|| expr->ts.type == BT_CHARACTER));
need_tmp = (gfc_ref_needs_temporary_p (expr->ref)
&& !subref_array_target);
@@ -8087,6 +8111,13 @@ gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr)
else
full = gfc_full_array_ref_p (info->ref, NULL);
+ /* A subobject of the array elements is described by a new descriptor,
+ whose element type is that of the subobject and whose span is the
+ element size of the array. */
+ if (subref_array_target && !se->direct_byref
+ && info->ref && info->ref->next)
+ full = 0;
+
if (full && !transposed_dims (ss))
{
if (se->direct_byref && !se->byref_noassign)
@@ -8408,8 +8439,10 @@ gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr)
}
else
{
- /* Otherwise make a new one. */
- if (expr->ts.type == BT_CHARACTER)
+ /* Otherwise make a new one. The element type is that of the
+ subobject for a subreference of the array. */
+ if (expr->ts.type == BT_CHARACTER
+ || (subref_array_target && !se->direct_byref))
parmtype = gfc_typenode_for_spec (&expr->ts);
else
parmtype = gfc_get_element_type (TREE_TYPE (desc));
diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
index 47b28c1d003..715dd1fd5c9 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -1408,6 +1408,11 @@ gfc_build_dummy_array_decl (gfc_symbol * sym, tree dummy)
GFC_DECL_SAVED_DESCRIPTOR (decl) = dummy;
+ /* The elements of the actual argument can be spaced by more than the
+ element size, so the span of the descriptor is used to address them. */
+ if (gfc_is_span_addressed_dummy (sym) && packed == PACKED_NO)
+ GFC_DECL_PTR_ARRAY_P (decl) = 1;
+
if (sym->ns->proc_name->backend_decl == current_function_decl
|| sym->attr.contained)
gfc_add_decl_to_function (decl);
@@ -1786,7 +1791,8 @@ gfc_get_symbol_decl (gfc_symbol * sym)
&& sym->attr.allocatable)
gfc_defer_symbol_init (sym);
- if (sym->attr.pointer && sym->attr.dimension && sym->ts.type != BT_CLASS)
+ if ((sym->attr.pointer && sym->attr.dimension && sym->ts.type != BT_CLASS)
+ || gfc_is_span_addressed_dummy (sym))
GFC_DECL_PTR_ARRAY_P (sym->backend_decl) = 1;
/* Create a character length variable. */
@@ -2078,6 +2084,20 @@ gfc_get_symbol_decl (gfc_symbol * sym)
&& !sym->attr.subref_array_pointer))
GFC_DECL_PTR_ARRAY_P (decl) = 1;
+ /* A SELECT RANK temporary uses a copy of the selector's descriptor.
+ Its elements may be spaced by more than the element size,
+ so use copied span as well. */
+ if (sym->attr.select_rank_temporary && sym->attr.dimension
+ && GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (decl))
+ && sym->assoc && sym->assoc->target
+ && sym->assoc->target->expr_type == EXPR_VARIABLE)
+ {
+ gfc_symbol *sel = sym->assoc->target->symtree->n.sym;
+ if (!sel->attr.contiguous
+ && (sel->attr.target || sel->attr.pointer || sel->ts.type == BT_CLASS))
+ GFC_DECL_PTR_ARRAY_P (decl) = 1;
+ }
+
if (sym->ts.type == BT_CLASS)
GFC_DECL_CLASS(decl) = 1;
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 33b7838f74a..8a72aafb016 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -837,6 +837,8 @@ gfc_class_array_data_assign (stmtblock_t *block, tree lhs_desc, tree rhs_desc,
gfc_conv_descriptor_dtype_set (block, lhs_desc,
gfc_conv_descriptor_dtype_get (rhs_desc));
+ gfc_conv_descriptor_span_set (block, lhs_desc,
+ gfc_conv_descriptor_span_get (rhs_desc));
/* Assign the dimension as range-ref. */
lhs_dim = gfc_get_descriptor_dimension (lhs_desc);
@@ -6233,14 +6235,12 @@ gfc_conv_gfc_desc_to_cfi_desc (gfc_se *parmse, gfc_expr *e, gfc_symbol *fsym)
else
gfc_conv_expr_descriptor (&se, e);
gfc = se.expr;
- /* For dt(:)%var the elem_len*stride != sm, hence, GFC uses
- elem_len = sizeof(dt) and base_addr = dt(lb) instead.
- gfc_get_dataptr_offset fixes the base_addr; for elem_len, see below.
- While sm is fine as it uses span*stride and not elem_len. */
+ /* For dt(:)%var, the base_addr is that of the subobject and elem_len is
+ its size, see below. The descriptor built for a subreference of the
+ array provides both. While sm is fine as it uses span*stride and not
+ elem_len. */
if (POINTER_TYPE_P (TREE_TYPE (gfc)))
gfc = build_fold_indirect_ref_loc (input_location, gfc);
- else if (is_subref_array (e) && e->ts.type != BT_CHARACTER)
- gfc_get_dataptr_offset (&se.pre, gfc, gfc, NULL, true, e);
}
if (e->ts.type == BT_CHARACTER)
{
@@ -6954,6 +6954,48 @@ conv_null_actual (gfc_se * parmse, gfc_expr * e, gfc_symbol * fsym)
}
+/* Return true if a subobject of the elements of an array is referenced. */
+
+static bool
+is_subobject_ref (gfc_expr *e)
+{
+ bool seen_array = false;
+
+ for (gfc_ref *ref = e->ref; ref; ref = ref->next)
+ {
+ if (ref->type == REF_ARRAY && ref->u.ar.type != AR_ELEMENT)
+ seen_array = true;
+ else if (seen_array)
+ return true;
+ }
+
+ return false;
+}
+
+
+/* Return true if the actual argument E for the dummy FSYM may be passed as a
+ copy-in/copy-out temporary. A pointer associated with a TARGET or POINTER
+ dummy must remain valid after the call, so the actual argument is passed
+ directly, with a descriptor whose span provides the element spacing. An
+ actual argument with a vector subscript is not definable and its pointer
+ association is undefined on return, so it is still copied. */
+
+static bool
+copy_in_out_allowed (gfc_symbol *fsym, gfc_expr *e, bool nodesc_arg)
+{
+ if (fsym == NULL || nodesc_arg || gfc_has_vector_subscript (e))
+ return true;
+
+ if (gfc_is_span_addressed_dummy (fsym))
+ return false;
+
+ return !(fsym->attr.pointer && !fsym->attr.contiguous && fsym->as
+ && (fsym->as->type == AS_ASSUMED_SHAPE
+ || fsym->as->type == AS_ASSUMED_RANK
+ || fsym->as->type == AS_DEFERRED));
+}
+
+
/* Generate code for a procedure call. Note can return se->post != NULL.
If se->direct_byref is set then se->expr contains the return parameter.
Return nonzero, if the call has alternate specifiers.
@@ -7985,14 +8027,20 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
else if (e->expr_type == EXPR_VARIABLE
&& is_subref_array (e)
- && !(fsym && fsym->attr.pointer))
+ && !(fsym && fsym->attr.pointer)
+ && copy_in_out_allowed (fsym, e, nodesc_arg))
/* The actual argument is a component reference to an
array of derived types. In this case, the argument
is converted to a temporary, which is passed and then
- written back after the procedure call. */
+ written back after the procedure call. The elements of
+ a span addressed dummy passed on as a whole are usually
+ contiguous, so the copy is made conditional. */
gfc_conv_subref_array_arg (&parmse, e, nodesc_arg,
fsym ? fsym->attr.intent : INTENT_INOUT,
- fsym && fsym->attr.pointer);
+ fsym && fsym->attr.pointer, fsym, sym->name,
+ NULL,
+ gfc_is_span_addressed_dummy (e->symtree->n.sym)
+ && !is_subobject_ref (e));
else if (e->ts.type == BT_CLASS && CLASS_DATA (e)->as
&& CLASS_DATA (e)->as->type == AS_ASSUMED_SIZE
@@ -8004,20 +8052,19 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
parmse.expr = e->symtree->n.sym->backend_decl;
else if (gfc_is_class_array_ref (e, NULL)
- && fsym && fsym->ts.type == BT_DERIVED)
+ && fsym && fsym->ts.type == BT_DERIVED
+ && copy_in_out_allowed (fsym, e, nodesc_arg))
/* The actual argument is a component reference to an
array of derived types. In this case, the argument
is converted to a temporary, which is passed and then
- written back after the procedure call.
- OOP-TODO: Insert code so that if the dynamic type is
- the same as the declared type, copy-in/copy-out does
- not occur. */
+ written back after the procedure call. */
gfc_conv_subref_array_arg (&parmse, e, nodesc_arg,
fsym->attr.intent,
fsym->attr.pointer);
else if (gfc_is_class_array_function (e)
- && fsym && fsym->ts.type == BT_DERIVED)
+ && fsym && fsym->ts.type == BT_DERIVED
+ && copy_in_out_allowed (fsym, e, nodesc_arg))
/* See previous comment. For function actual argument,
the write out is not needed so the intent is set as
intent in. */
@@ -8038,10 +8085,17 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
fsym->attr.pointer);
}
else
- /* This is where we introduce a temporary to store the
- result of a non-lvalue array expression. */
- gfc_conv_array_parameter (&parmse, e, nodesc_arg, fsym,
- sym->name, NULL);
+ {
+ /* Having declined copy-in/copy-out above, a subobject of an
+ array is described by a spanned descriptor. */
+ if (e->expr_type == EXPR_VARIABLE && is_subref_array (e))
+ parmse.force_no_tmp = 1;
+
+ /* This is where we introduce a temporary to store the
+ result of a non-lvalue array expression. */
+ gfc_conv_array_parameter (&parmse, e, nodesc_arg, fsym,
+ sym->name, NULL);
+ }
/* If an ALLOCATABLE dummy argument has INTENT(OUT) and is
allocated on entry, it must be deallocated.
diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index 5e3681da467..a76d9bb4325 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -2385,9 +2385,10 @@ gfc_conv_is_contiguous_expr (gfc_se *se, gfc_expr *arg)
se->expr = cond;
}
- /* A pointer that does not have the CONTIGUOUS attribute needs to be checked
- if it points to an array whose span differs from the element size. */
- if (as && sym && IS_POINTER(sym) && !sym->attr.contiguous)
+ /* An array that is addressed by the span of its descriptor needs to be
+ checked if that span differs from the element size. */
+ if (as && sym && !sym->attr.contiguous
+ && (IS_POINTER (sym) || gfc_is_span_addressed_dummy (sym)))
{
tree span = gfc_conv_descriptor_span_get (desc);
tmp = fold_convert (TREE_TYPE (span),
diff --git a/gcc/fortran/trans.cc b/gcc/fortran/trans.cc
index cf37261673c..c2ad65c9a74 100644
--- a/gcc/fortran/trans.cc
+++ b/gcc/fortran/trans.cc
@@ -389,6 +389,27 @@ gfc_build_addr_expr (tree type, tree t)
}
+/* Return the descriptor that carries the span of DECL, which is marked as a
+ pointer array. Such a decl usually is a descriptor. The local decl of a
+ descriptorless dummy array is not, so its span comes from the descriptor it
+ was built from, which is the saved one. */
+
+tree
+gfc_get_span_descriptor (tree decl)
+{
+ if (DECL_P (decl)
+ && GFC_ARRAY_TYPE_P (TREE_TYPE (decl))
+ && DECL_LANG_SPECIFIC (decl)
+ && GFC_DECL_SAVED_DESCRIPTOR (decl))
+ decl = GFC_DECL_SAVED_DESCRIPTOR (decl);
+
+ if (POINTER_TYPE_P (TREE_TYPE (decl)))
+ decl = build_fold_indirect_ref_loc (input_location, decl);
+
+ return decl;
+}
+
+
static tree
get_array_span (tree type, tree decl)
{
@@ -409,7 +430,9 @@ get_array_span (tree type, tree decl)
&& (TREE_CODE (type) == ARRAY_TYPE || TREE_CODE (type) == INTEGER_TYPE)
&& TYPE_STRING_FLAG (type))
{
- if (TREE_CODE (decl) == PARM_DECL)
+ if (DECL_P (decl) && GFC_DECL_PTR_ARRAY_P (decl))
+ decl = gfc_get_span_descriptor (decl);
+ else if (TREE_CODE (decl) == PARM_DECL)
decl = build_fold_indirect_ref_loc (input_location, decl);
if (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (decl)))
span = gfc_conv_descriptor_span_get (decl);
@@ -449,11 +472,7 @@ get_array_span (tree type, tree decl)
span = gfc_resize_class_size_with_len (NULL, decl, span);
}
else if (GFC_DECL_PTR_ARRAY_P (decl))
- {
- if (TREE_CODE (decl) == PARM_DECL)
- decl = build_fold_indirect_ref_loc (input_location, decl);
- span = gfc_conv_descriptor_span_get (decl);
- }
+ span = gfc_conv_descriptor_span_get (gfc_get_span_descriptor (decl));
else
span = NULL_TREE;
}
diff --git a/gcc/fortran/trans.h b/gcc/fortran/trans.h
index 0bdee5820fd..408acf081f1 100644
--- a/gcc/fortran/trans.h
+++ b/gcc/fortran/trans.h
@@ -641,6 +641,9 @@ tree gfc_build_array_ref (tree, tree, tree,
/* Build an array ref using pointer arithmetic. */
tree gfc_build_spanned_array_ref (tree base, tree offset, tree span);
+/* Return the descriptor holding the span of a pointer array decl. */
+tree gfc_get_span_descriptor (tree);
+
/* Creates a label. Decl is artificial if label_id == NULL_TREE. */
tree gfc_build_label_decl (tree);
diff --git a/gcc/testsuite/gfortran.dg/c_loc_test_22.f90 b/gcc/testsuite/gfortran.dg/c_loc_test_22.f90
index 7b1149aaa45..91547e8e337 100644
--- a/gcc/testsuite/gfortran.dg/c_loc_test_22.f90
+++ b/gcc/testsuite/gfortran.dg/c_loc_test_22.f90
@@ -17,7 +17,9 @@ end
! { dg-final { scan-tree-dump-not " _gfortran_internal_pack" "original" } }
! { dg-final { scan-tree-dump-times "parm.\[0-9\]+.data = \\(void .\\) &\\(.xxx.\[0-9\]+\\)\\\[0\\\];" 1 "original" } }
! { dg-final { scan-tree-dump-times "parm.\[0-9\]+.data = \\(void .\\) &\\(.xxx.\[0-9\]+\\)\\\[D.\[0-9\]+ \\* 4\\\];" 1 "original" } }
-! { dg-final { scan-tree-dump-times "parm.\[0-9\]+.data = \\(void .\\) yyy.\[0-9\]+;" 1 "original" } }
-! { dg-final { scan-tree-dump-times "parm.\[0-9\]+.data = \\(void .\\) yyy.\[0-9\]+ \\+ \\(sizetype\\) \\(D.\[0-9\]+ \\* 16\\);" 1 "original" } }
+! A TARGET assumed-shape dummy is addressed with the descriptor's runtime
+! span, so the element offset is span-scaled instead of a constant 16.
+! { dg-final { scan-tree-dump-times "parm.\[0-9\]+.data = \\(void .\\) &\\(.yyy.\[0-9\]+\\)\\\[0\\\];" 1 "original" } }
+! { dg-final { scan-tree-dump-times "parm.\[0-9\]+.data = \\(void .\\) yyy.\[0-9\]+ \\+ \\(sizetype\\) \\(\\(yyy->span \\* D.\[0-9\]+\\) \\* 4\\);" 1 "original" } }
! { dg-final { scan-tree-dump-times "D.\[0-9\]+ = parm.\[0-9\]+.data;\[^;]+ptr\[1-4\] = D.\[0-9\]+;" 4 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/class_to_type_5.f90 b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
new file mode 100644
index 00000000000..ad299db514d
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
@@ -0,0 +1,35 @@
+! { dg-do run }
+! PR 53800
+
+! Check that a CLASS array with an extended dynamic type passed to an
+! assumed-shape TYPE dummy aliases the original storage, rather
+! than a copy-in/copy-out temporary that goes stale after return.
+!
+! Reported by Tobias Burnus <[email protected]>
+
+program class_to_type
+ implicit none
+ type t
+ integer :: i
+ end type t
+ type, extends(t) :: t2
+ integer :: j
+ end type t2
+ class(t), target, allocatable :: a(:,:)
+ type(t), pointer :: ptr
+
+ allocate (t2 :: a(5,5))
+ a(:,:)%i = 53
+ a(3,3)%i = 42
+ a(4,4)%i = 74
+
+ call f (a)
+ if (ptr%i /= 42) stop 1
+ a(3,3)%i = 999
+ if (ptr%i /= 999) stop 2
+contains
+ subroutine f(x)
+ type(t), target :: x(:,:)
+ ptr => x(3,3)
+ end subroutine f
+end program class_to_type
diff --git a/gcc/testsuite/gfortran.dg/class_to_type_6.f90 b/gcc/testsuite/gfortran.dg/class_to_type_6.f90
new file mode 100644
index 00000000000..67d02c67fb8
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/class_to_type_6.f90
@@ -0,0 +1,93 @@
+! { dg-do run }
+! PR53800
+
+! A CLASS array actual passed to an assumed-shape TYPE dummy only
+! aliases the actual's storage when the dummy has the TARGET attribute.
+!
+module m
+ implicit none
+ type :: t
+ integer :: i
+ end type
+ type, extends(t) :: t2
+ integer :: pad(4)
+ end type
+ type :: u
+ integer :: k
+ end type
+ type :: c
+ integer :: i
+ type(u) :: sub(3)
+ end type
+ type, extends(c) :: c2
+ integer :: pad(4)
+ end type
+contains
+ ! A dummy (non-target): copy-in/copy-out,
+ subroutine plain (x)
+ type(t) :: x(:)
+ if (any (x%i /= [1,2,3,4,5])) stop 1
+ call expl (x)
+ if (any (cshift (x%i, 1) /= [2,3,4,5,1])) stop 3
+ if (any (pack (x%i, [.true.,.false.,.true.,.false.,.true.]) &
+ /= [1,3,5])) stop 4
+ if (any (reshape (x%i, [1,5]) /= reshape ([1,2,3,4,5], [1,5]))) stop 5
+ call to_class (x)
+ end subroutine
+
+ subroutine expl (y)
+ type(t) :: y(5)
+ if (any (y%i /= [1,2,3,4,5])) stop 2
+ end subroutine
+
+ subroutine to_class (z)
+ class(t) :: z(:)
+ if (any (z%i /= [1,2,3,4,5])) stop 6
+ end subroutine
+
+ ! A component sub-array of a span-carrying dummy has its own element
+ ! size and must not inherit the parent's span.
+ subroutine comp (x)
+ type(c), target :: x(:)
+ call inner (x(2)%sub)
+ end subroutine
+
+ subroutine inner (s)
+ type(u) :: s(:)
+ if (any (s%k /= [21,22,23])) stop 7
+ end subroutine
+end module
+
+program class_to_type_6
+ use m
+ implicit none
+ class(t), target, allocatable :: a(:)
+ class(c), target, allocatable :: b(:)
+ type(t), pointer :: p
+ integer :: n
+
+ allocate (t2 :: a(5))
+ do n = 1, 5
+ a(n)%i = n
+ end do
+ call plain (a)
+
+ allocate (c2 :: b(3))
+ do n = 1, 3
+ b(n)%i = 10 * n
+ b(n)%sub(:)%k = [10*n+1, 10*n+2, 10*n+3]
+ end do
+ call comp (b)
+
+ ! A TARGET assumed-shape dummy without CONTIGUOUS does alias.
+ call aliased (a)
+ if (p%i /= 3) stop 8
+ a(3)%i = 999
+ if (p%i /= 999) stop 9
+
+contains
+ subroutine aliased (x)
+ type(t), target :: x(:)
+ p => x(3)
+ end subroutine
+end program
diff --git a/gcc/testsuite/gfortran.dg/class_to_type_7.f90 b/gcc/testsuite/gfortran.dg/class_to_type_7.f90
new file mode 100644
index 00000000000..c5f74809c46
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/class_to_type_7.f90
@@ -0,0 +1,151 @@
+! { dg-do run }
+! PR fortran/53800
+
+! Further cases in which a dummy must be associated with the actual
+! argument's storage rather than a copy-in/copy-out temporary: an
+! intrinsic-type component of a CLASS array, a POINTER dummy and an
+! assumed-rank TARGET dummy.
+!
+! Variations contributed by Mikael Morin <[email protected]>
+
+module m
+ implicit none
+ type t
+ integer :: i
+ end type t
+ type, extends(t) :: t2
+ integer :: j
+ end type t2
+end module m
+
+! An intrinsic-type component of a CLASS array to an INTEGER TARGET dummy.
+subroutine test_integer_component ()
+ use m
+ implicit none
+ class(t), target, allocatable :: a(:,:)
+ integer, pointer :: ptr
+
+ allocate (t2 :: a(5,5))
+ a(:,:)%i = 53
+ a(3,3)%i = 42
+
+ call f (a%i)
+ if (ptr /= 42) stop 1
+ a(3,3)%i = 999
+ if (ptr /= 999) stop 2
+contains
+ subroutine f(x)
+ integer, target :: x(:,:)
+ ptr => x(3,3)
+ end subroutine f
+end subroutine test_integer_component
+
+! A component of a plain derived-type array to an INTEGER TARGET dummy.
+subroutine test_subref_component ()
+ implicit none
+ type u
+ integer :: i
+ integer :: pad
+ end type u
+ type(u), target :: a(5,5)
+ integer, pointer :: ptr
+
+ a(:,:)%i = 53
+ a(3,3)%i = 42
+
+ call f (a%i)
+ if (ptr /= 42) stop 3
+ a(3,3)%i = 999
+ if (ptr /= 999) stop 4
+contains
+ subroutine f(x)
+ integer, target :: x(:,:)
+ ptr => x(3,3)
+ end subroutine f
+end subroutine test_subref_component
+
+! A character component of a derived-type array to a CHARACTER TARGET dummy.
+subroutine test_character_component ()
+ implicit none
+ type u
+ character(len=4) :: c
+ integer :: pad
+ end type u
+ type(u), target :: a(6)
+ character(len=4), pointer :: ptr
+ integer :: k
+
+ do k = 1, 6
+ a(k)%c = "ab00"
+ end do
+ a(4)%c = "zzzz"
+
+ call f (a%c)
+ if (ptr /= "zzzz") stop 10
+ a(4)%c = "qqqq"
+ if (ptr /= "qqqq") stop 11
+contains
+ subroutine f(x)
+ character(len=4), target :: x(:)
+ ptr => x(4)
+ end subroutine f
+end subroutine test_character_component
+
+! A CLASS POINTER array to a TYPE POINTER dummy.
+subroutine test_pointer_dummy ()
+ use m
+ implicit none
+ class(t), pointer :: a(:,:)
+ type(t), pointer :: ptr
+
+ allocate (t2 :: a(5,5))
+ a(:,:)%i = 53
+ a(3,3)%i = 42
+
+ call f (a)
+ if (ptr%i /= 42) stop 5
+ a(3,3)%i = 999
+ if (ptr%i /= 999) stop 6
+ deallocate (a)
+contains
+ subroutine f(x)
+ type(t), pointer :: x(:,:)
+ ptr => x(3,3)
+ end subroutine f
+end subroutine test_pointer_dummy
+
+! A CLASS array to an assumed-rank TARGET dummy, selected with SELECT RANK.
+subroutine test_assumed_rank ()
+ use m
+ implicit none
+ class(t), target, allocatable :: a(:,:)
+ type(t), pointer :: ptr
+
+ allocate (t2 :: a(5,5))
+ a(:,:)%i = 53
+ a(3,3)%i = 42
+
+ call f (a)
+ if (ptr%i /= 42) stop 7
+ a(3,3)%i = 999
+ if (ptr%i /= 999) stop 8
+contains
+ subroutine f(x)
+ type(t), target :: x(..)
+ select rank (x)
+ rank (2)
+ ptr => x(3,3)
+ rank default
+ error stop 9
+ end select
+ end subroutine f
+end subroutine test_assumed_rank
+
+program class_to_type_7
+ implicit none
+ call test_integer_component ()
+ call test_subref_component ()
+ call test_character_component ()
+ call test_pointer_dummy ()
+ call test_assumed_rank ()
+end program class_to_type_7
diff --git a/gcc/testsuite/gfortran.dg/class_to_type_8.f90 b/gcc/testsuite/gfortran.dg/class_to_type_8.f90
new file mode 100644
index 00000000000..48851d42cd1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/class_to_type_8.f90
@@ -0,0 +1,47 @@
+! { dg-do run }
+! PR fortran/53800
+
+! A component of an array of extended derived types, passed to a TARGET
+! dummy, is addressed through the span of its descriptor. This must also
+! hold for a section of such a dummy and for a subobject reference applied
+! to it.
+!
+! Contributed by Mikael Morin <[email protected]>
+
+program p
+ implicit none
+ type :: t
+ integer :: c1, c2
+ end type
+ type, extends(t) :: u
+ integer :: c3
+ end type
+ type, extends(u) :: v
+ integer :: c4
+ end type
+ type(v), target :: x(12)
+ integer :: i
+ x = [(v(i,i*i,i,i), i=1,size(x))]
+ call s1(x(2::3)%c2, 1)
+ call s2(x%u)
+contains
+ subroutine s1(a, error_idx)
+ integer, intent(in) :: error_idx
+ integer, target :: a(:)
+ if (any(a /= [4, 25, 64, 121])) error stop error_idx * 10 + 1
+ end subroutine
+ subroutine s2(a)
+ type(u), target :: a(:)
+ if (any(a%c2 /= [(i*i, i=1,12)])) error stop 2
+ if (any(a(2::3)%c2 /= [4, 25, 64, 121])) error stop 3
+ call s1(a(2::3)%c2, 2)
+ call s3(a(2::3)%c2)
+ if (any(x(2::3)%c2 /= [-4, -25, -64, -121])) error stop 5
+ x = [(v(i,i*i,i,i), i=1,size(x))]
+ end subroutine
+ subroutine s3(a)
+ integer :: a(:) ! copy-in/copy-out
+ if (any(a /= [4, 25, 64, 121])) error stop 4
+ a = -a
+ end subroutine
+end program
diff --git a/gcc/testsuite/gfortran.dg/class_to_type_9.f90 b/gcc/testsuite/gfortran.dg/class_to_type_9.f90
new file mode 100644
index 00000000000..40f4e6a53de
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/class_to_type_9.f90
@@ -0,0 +1,67 @@
+! { dg-do run }
+! PR fortran/53800
+
+! A TARGET dummy associated with elements that are spaced by more than the
+! element size: pointers to it stay valid after the call, it is written
+! through, it is not contiguous, it is copied when passed on to a dummy
+! without the TARGET attribute and it is transferred element by element.
+
+module m
+ implicit none
+ type :: t
+ integer :: c1, c2
+ end type
+ integer, pointer :: saved(:) => null()
+contains
+ subroutine chk(a, opt)
+ integer, target :: a(:)
+ integer, optional, target :: opt(:)
+ character(24) :: line
+ if (is_contiguous(a)) stop 1
+ if (present(opt)) stop 2
+ write (line, '(4I3)') a
+ if (line(1:12) /= ' 1 4 9 16') stop 3
+ if (sum(a) /= 30) stop 4
+ call packed(a)
+ call assumed_size(a)
+ saved => a
+ a(2) = -a(2)
+ end subroutine
+ subroutine packed(b) ! copy-in/copy-out
+ integer :: b(:)
+ if (any(b /= [1, 4, 9, 16])) stop 5
+ if (.not. is_contiguous(b)) stop 6
+ end subroutine
+ subroutine assumed_size(c) ! no descriptor
+ integer :: c(*)
+ if (any(c(1:4) /= [1, 4, 9, 16])) stop 7
+ end subroutine
+ subroutine rank_any(d)
+ integer, target :: d(..)
+ select rank (d)
+ rank (1)
+ if (any(d /= [1, 2, 3, 4])) stop 8
+ saved => d
+ rank default
+ stop 9
+ end select
+ end subroutine
+end module
+
+program p
+ use m
+ implicit none
+ type(t), target :: x(4)
+ integer :: i
+ x = [(t(i, i*i), i=1,4)]
+
+ call chk(x%c2)
+ if (any(x%c2 /= [1, -4, 9, 16])) stop 10
+ saved = 0
+ if (any(x%c2 /= [0, 0, 0, 0])) stop 11
+ if (any(x%c1 /= [1, 2, 3, 4])) stop 12
+
+ call rank_any(x%c1)
+ saved = 7
+ if (any(x%c1 /= [7, 7, 7, 7])) stop 13
+end program
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/host_data-5.F90 b/libgomp/testsuite/libgomp.oacc-fortran/host_data-5.F90
index c3453a579ae..ae5104beb48 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/host_data-5.F90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/host_data-5.F90
@@ -74,10 +74,17 @@ subroutine foo (p2, parr, host_p, host_parr, cond)
! { dg-note {variable 'host_p\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-5 }
! { dg-note {variable 'host_parr\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-6 }
! { dg-note {variable 'C\.[0-9]+' declared in block potentially has improper OpenACC privatization level: 'const_decl'} "TODO" { target *-*-* } .-7 }
- ! { dg-note {variable 'parm\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: artificial} "" { target *-*-* } .-8 }
- ! { dg-note {variable 'D\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: artificial} "" { target *-*-* } .-9 }
- ! { dg-note {variable 'transfer\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: artificial} "" { target *-*-* } .-10 }
- ! { dg-note {variable 'parm\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-11 }
+ ! { dg-note {variable 'D\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: artificial} "" { target *-*-* } .-8 }
+ ! { dg-note {variable 'transfer\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: artificial} "" { target *-*-* } .-9 }
+ ! { dg-note {variable 'parm\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-10 }
+ ! The TARGET dummy 'parr' is addressed by the span of its descriptor, so
+ ! passing it to a dummy without a descriptor packs it when it is not
+ ! contiguous.
+ ! { dg-note {variable 'iftmp\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-14 }
+ ! { dg-note {variable 'atmp\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-15 }
+ ! { dg-note {variable 'arg_ptr\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-16 }
+ ! { dg-note {variable 'contiguous\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-17 }
+ ! { dg-note {variable 'S\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} "" { target *-*-* } .-18 }
if (.not. acc_is_present(p, c_sizeof(p))) stop 11
if (.not. acc_is_present(parr, 1)) stop 12
! Not inside a host_data construct, so still the host pointer.
--
2.55.0