See the following attached patch.
This was actually a nasty problem. It could only be seen if the testcase that
failed happened to be written in a certain way and only showed up with -m32.
I tested this patch on x86_64. The failure also reproduces with an
arm-linux-gnueabihf cross compiler (same 6 errors in associate_91); the patch
fixes it there as well..
OK for mainline?
Regards,
Jerry
---
[PATCH] fortran: [PR126968] Make inferred ASSOCIATE type guess
target-independent
The test associate_91.f90, added for PR126968, failed with -m32.
When an ASSOCIATE selector is a function that has not been parsed yet,
gfc_match_varspec guesses the type of the associate name from the first
component reference. Any accessible derived type with a component of
that name is a candidate, and the first candidate is used. The
candidates were collected by a pre-order walk of the symtree. The shape
of the symtree depends on how many symbols were inserted before, which
differs between targets. With -m32 the guess for associate_91 became a
type in which the component is a scalar integer, so the rest of the
reference, q%c1(4)%c2(:)%bar, could not be parsed.
Pick the first candidate in which the whole component chain exists, and
collect the candidates by an in-order walk so that the guess no longer
depends on the target.
With the guess now predictable, associate_93.f90 is changed so that both
of its blocks start from a wrong guess. This exposed a case that was
never exercised on x86_64: when the guessed component is a scalar
character, a following section such as t(2:3) is parsed as a substring.
When the real component is an array, turn the substring back into an
array section.
PR fortran/126968
gcc/fortran/ChangeLog:
* primary.cc (skip_ref_groups): New function.
(inferred_type_chain_ok): New function.
(gfc_match_varspec): Use it to choose among the candidate types of
an inferred-type associate name.
* resolve.cc (fixup_inferred_component_array_ref): Turn a substring
into an array section when the component is a rank-1 array.
* symbol.cc (find_derived_types): Walk the symtree in order.
gcc/testsuite/ChangeLog:
* gfortran.dg/associate_93.f90: Rename the guessed types so that
they are tried first.
* gfortran.dg/associate_97.f90: New test.
---From 64d3acbaabd74ae2648afd805b6ad7378ad439e2 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Tue, 27 Oct 2026 10:50:08 -0400
Subject: [PATCH] fortran: [PR126968] Make inferred ASSOCIATE type guess
target-independent
The test associate_91.f90, added for PR126968, failed with -m32.
When an ASSOCIATE selector is a function that has not been parsed yet,
gfc_match_varspec guesses the type of the associate name from the first
component reference. Any accessible derived type with a component of
that name is a candidate, and the first candidate is used. The
candidates were collected by a pre-order walk of the symtree. The shape
of the symtree depends on how many symbols were inserted before, which
differs between targets. With -m32 the guess for associate_91 became a
type in which the component is a scalar integer, so the rest of the
reference, q%c1(4)%c2(:)%bar, could not be parsed.
Pick the first candidate in which the whole component chain exists, and
collect the candidates by an in-order walk so that the guess no longer
depends on the target.
With the guess now predictable, associate_93.f90 is changed so that both
of its blocks start from a wrong guess. This exposed a case that was
never exercised on x86_64: when the guessed component is a scalar
character, a following section such as t(2:3) is parsed as a substring.
When the real component is an array, turn the substring back into an
array section.
PR fortran/126968
gcc/fortran/ChangeLog:
* primary.cc (skip_ref_groups): New function.
(inferred_type_chain_ok): New function.
(gfc_match_varspec): Use it to choose among the candidate types of
an inferred-type associate name.
* resolve.cc (fixup_inferred_component_array_ref): Turn a substring
into an array section when the component is a rank-1 array.
* symbol.cc (find_derived_types): Walk the symtree in order.
gcc/testsuite/ChangeLog:
* gfortran.dg/associate_93.f90: Rename the guessed types so that
they are tried first.
* gfortran.dg/associate_97.f90: New test.
---
gcc/fortran/primary.cc | 110 ++++++++++++++++++++-
gcc/fortran/resolve.cc | 15 +++
gcc/fortran/symbol.cc | 7 +-
gcc/testsuite/gfortran.dg/associate_93.f90 | 6 +-
gcc/testsuite/gfortran.dg/associate_97.f90 | 67 +++++++++++++
5 files changed, 195 insertions(+), 10 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/associate_97.f90
diff --git a/gcc/fortran/primary.cc b/gcc/fortran/primary.cc
index d7a880b3983..7b951d77619 100644
--- a/gcc/fortran/primary.cc
+++ b/gcc/fortran/primary.cc
@@ -2173,6 +2173,99 @@ is_inquiry_ref (const char *name, gfc_ref **ref)
}
+/* Skip any parenthesized or bracketed groups at the current locus. */
+
+static void
+skip_ref_groups (void)
+{
+ for (;;)
+ {
+ gfc_gobble_whitespace ();
+ gfc_char_t c = gfc_peek_char ();
+ if (c != '(' && c != '[')
+ return;
+
+ int depth = 0;
+ gfc_char_t quote = ' ';
+ do
+ {
+ c = gfc_next_char_literal (quote == ' ' ? NONSTRING
+ : INSTRING_NOWARN);
+ if (c == '\n')
+ return;
+ if (quote != ' ')
+ {
+ if (c == quote)
+ quote = ' ';
+ }
+ else if (c == '\'' || c == '"')
+ quote = c;
+ else if (c == '(' || c == '[')
+ depth++;
+ else if (c == ')' || c == ']')
+ depth--;
+ }
+ while (depth > 0);
+ }
+}
+
+
+/* Return true if the component references at the current locus can be
+ followed starting from derived type DT. The locus is not changed. */
+
+static bool
+inferred_type_chain_ok (gfc_symbol *dt)
+{
+ char name[GFC_MAX_SYMBOL_LEN + 1];
+ locus old_loc = gfc_current_locus;
+ bool ok = true;
+
+ while (gfc_match_name (name) == MATCH_YES)
+ {
+ gfc_component *c = gfc_find_component (dt, name, true, true, NULL);
+ if (!c)
+ {
+ ok = dt->f2k_derived
+ && gfc_find_typebound_proc (dt, NULL, name, true, NULL);
+ break;
+ }
+
+ /* A subscript needs an array, a substring or a procedure pointer. */
+ gfc_gobble_whitespace ();
+ if (gfc_peek_ascii_char () == '('
+ && !c->as && c->ts.type != BT_CHARACTER && !c->attr.proc_pointer
+ && !(c->ts.type == BT_CLASS && c->attr.class_ok
+ && CLASS_DATA (c)->as))
+ {
+ ok = false;
+ break;
+ }
+ skip_ref_groups ();
+
+ if (gfc_match_char ('%') != MATCH_YES)
+ break;
+
+ if (c->ts.type == BT_DERIVED)
+ dt = c->ts.u.derived;
+ else if (c->ts.type == BT_CLASS)
+ {
+ if (!c->attr.class_ok || !CLASS_DATA (c))
+ break;
+ dt = CLASS_DATA (c)->ts.u.derived;
+ }
+ else
+ {
+ ok = gfc_match_name (name) == MATCH_YES
+ && is_inquiry_ref (name, NULL);
+ break;
+ }
+ }
+
+ gfc_current_locus = old_loc;
+ return ok;
+}
+
+
/* Check to see if functions in operator expressions can be resolved now. */
static bool
@@ -2456,6 +2549,7 @@ gfc_match_varspec (gfc_expr *primary, int equiv_flag, bool sub_flag,
{
bool sym_present, resolved = false;
gfc_symbol *tgt_sym;
+ int ndt;
sym_present = tgt_expr && tgt_expr->symtree && tgt_expr->symtree->n.sym;
tgt_sym = sym_present ? tgt_expr->symtree->n.sym : NULL;
@@ -2492,15 +2586,23 @@ gfc_match_varspec (gfc_expr *primary, int equiv_flag, bool sub_flag,
|| (!resolved && tgt_expr->expr_type == EXPR_OP))
&& (sym->ts.type == BT_UNKNOWN
|| (inferred_type && sym->ts.type != BT_COMPLEX))
- && gfc_find_derived_types (sym, gfc_current_ns, name, true))
+ && (ndt = gfc_find_derived_types (sym, gfc_current_ns, name, true)))
{
sym->assoc->inferred_type = 1;
- /* The first returned type is as good as any at this stage. The final
- determination is made in 'gfc_fixup_inferred_type_refs'*/
+ /* Pick a candidate type for now. The final determination is made in
+ 'gfc_fixup_inferred_type_refs'. Prefer the first candidate that
+ has the whole component chain. */
gfc_symbol **dts = &sym->assoc->derived_types;
+ gfc_symbol *dt = *dts;
+ for (gfc_symbol *cand = *dts; cand && ndt-- > 0; cand = cand->dt_next)
+ if (inferred_type_chain_ok (cand))
+ {
+ dt = cand;
+ break;
+ }
tgt_expr->ts.type = BT_DERIVED;
tgt_expr->ts.kind = 0;
- tgt_expr->ts.u.derived = *dts;
+ tgt_expr->ts.u.derived = dt;
sym->ts = tgt_expr->ts;
primary->ts = sym->ts;
/* Delete the dt list even if this process has to be done again for
diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index a6e05920e91..99bfbd00552 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -7048,6 +7048,21 @@ fixup_inferred_component_array_ref (gfc_expr *e, gfc_component *c,
/* Leave the array ref of a scalar to find_array_spec to diagnose. */
ref->u.ar.as = NULL;
}
+ else if (as && as->rank == 1 && ref && ref->type == REF_SUBSTRING)
+ {
+ /* An array section misidentified as a substring. */
+ gfc_expr *start = ref->u.ss.start;
+ gfc_expr *end = ref->u.ss.end;
+ memset (&ref->u, 0, sizeof (ref->u));
+ ref->type = REF_ARRAY;
+ ref->u.ar.type = AR_SECTION;
+ ref->u.ar.dimen = 1;
+ ref->u.ar.dimen_type[0] = DIMEN_RANGE;
+ ref->u.ar.start[0] = start;
+ ref->u.ar.end[0] = end;
+ ref->u.ar.as = as;
+ ref->u.ar.where = e->where;
+ }
else if (as)
{
/* Supply the full array ref of a component guessed to be scalar. */
diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc
index 73f0280645d..00c86041550 100644
--- a/gcc/fortran/symbol.cc
+++ b/gcc/fortran/symbol.cc
@@ -2494,6 +2494,10 @@ static void
find_derived_types (gfc_symbol *sym, gfc_symtree *st, const char *name,
bool contained, bool stash)
{
+ /* Visit in order so that the result does not depend on the tree shape. */
+ if (st->left)
+ find_derived_types (sym, st->left, name, contained, stash);
+
if (st->n.sym && st->n.sym->attr.flavor == FL_DERIVED
&& !st->n.sym->attr.is_class
&& ((contained && st->n.sym->attr.use_assoc) || !contained)
@@ -2513,9 +2517,6 @@ find_derived_types (gfc_symbol *sym, gfc_symtree *st, const char *name,
}
}
- if (st->left)
- find_derived_types (sym, st->left, name, contained, stash);
-
if (st->right)
find_derived_types (sym, st->right, name, contained, stash);
}
diff --git a/gcc/testsuite/gfortran.dg/associate_93.f90 b/gcc/testsuite/gfortran.dg/associate_93.f90
index aa370752e9d..43cecf19233 100644
--- a/gcc/testsuite/gfortran.dg/associate_93.f90
+++ b/gcc/testsuite/gfortran.dg/associate_93.f90
@@ -1,7 +1,7 @@
! { dg-do run }
! PR fortran/126968
! Array references after components of an associate name whose type was
-! guessed wrongly ('g*_t') must follow the array spec of the real component.
+! guessed wrongly ('w*_t') must follow the array spec of the real component.
!
module m
type :: leaf_t
@@ -10,7 +10,7 @@ module m
type :: w1_t
type(leaf_t) :: b(2)
end type
- type :: g_t
+ type :: w_t
type(w1_t) :: a(4)
integer :: v(3) = 9
character(4) :: s(3) = 'gggg'
@@ -25,7 +25,7 @@ module m
character(4) :: s = 'abcd'
integer :: m(2,2) = reshape ([1, 2, 3, 4], [2, 2])
end type
- type :: g2_t
+ type :: w2_t
integer :: w = 9
character(4) :: t = 'gggg'
class(leaf_t), allocatable :: c
diff --git a/gcc/testsuite/gfortran.dg/associate_97.f90 b/gcc/testsuite/gfortran.dg/associate_97.f90
new file mode 100644
index 00000000000..a7883f06d7a
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/associate_97.f90
@@ -0,0 +1,67 @@
+! { dg-do run }
+! PR fortran/126968
+! The parse-time type guess for an associate name must pick a candidate
+! type in which the whole component chain exists ('wrong_t' and 'zz_t' are
+! tried first).
+!
+module m
+ type :: leaf_t
+ integer :: bar = 0
+ complex :: z = (1.0, 2.0)
+ character(5) :: s = 'hello'
+ contains
+ procedure :: get
+ end type
+ type :: mid_t
+ class(leaf_t), allocatable :: c2(:)
+ end type
+ type :: outer_t
+ type(mid_t) :: c1(5)
+ end type
+ type :: wrong_t
+ integer :: c1 = 1
+ end type
+ type :: zz_t
+ character(8) :: c1 = 'zzzzzzzz'
+ end type
+contains
+ integer function get (this)
+ class(leaf_t), intent(in) :: this
+ get = this%bar + 100
+ end function
+end module
+
+program p
+ use m
+ implicit none
+ integer :: i, k
+ real :: r
+ character(3) :: c
+
+ associate (q => tfcn ())
+ i = q%c1(index ("a(b)c", "(") + 2)%c2(2)%bar
+ if (i /= 42) stop 1
+ i = q%c1(3)% &
+ c2(1)%bar
+ if (i /= 31) stop 2
+ r = q%c1(4)%c2(1)%z%im
+ if (r /= 2.0) stop 3
+ k = q%c1(4)%c2(3)%s%len
+ if (k /= 5) stop 4
+ c = q%c1(4)%c2(3)%s(2:4)
+ if (c /= 'ell') stop 5
+ i = q%c1(2)%c2(1)%get ()
+ if (i /= 121) stop 6
+ end associate
+
+contains
+
+ function tfcn () result (y)
+ type(outer_t) :: y
+ integer :: k
+ do k = 1, 5
+ allocate (y%c1(k)%c2(3))
+ y%c1(k)%c2(:)%bar = [10*k+1, 10*k+2, 10*k+3]
+ end do
+ end function
+end program
--
2.56.0