See attached patch.

This patch fixes a problem with nested DO_CONCURRENT loops. In short, shadow variables were not unique between the nested levels.

Regression tested on x86_64.

OK for mainline?

Regards,

Jerry

---

fortran: [PR125747] Fix shadow-variable bugs for DO
 CONCURRENT/FORALL type-spec

When a DO CONCURRENT or FORALL construct gives its index-name an
explicit type-spec (F2018 19.4(6)) and that name already exists in the
enclosing scope, the front end creates a "shadow" variable with the
requested type and substitutes references to the outer-scope variable
with the shadow inside the construct's body.  Three related bugs in
this mechanism could cause an ICE or wrong code:

1. The shadow variable was always named "_<name>", so two sibling
   constructs reusing the same index-name with different type-specs
   ended up sharing (and mutating) the same shadow symbol, corrupting
   already-resolved bounds and triggering an ICE in
   gfc_add_modify_loc.

2. gfc_resolve_forall derived the shadowed outer-scope symbol by
   stripping a single leading underscore from the shadow variable's
   name.  Once shadow names were uniquified (fix 1) this derivation
   broke for "__name", "___name", etc., causing the body substitution
   to target the wrong symbol and produce wrong runtime values.

3. The generic block-recursion in replace_in_code_recursive
   unconditionally descended into nested FORALL/DO CONCURRENT bodies
   during an outer construct's substitution pass, prematurely
   rewriting references to the shared index-name before the nested
   construct (which shadows the same name itself) performed its own
   substitution.

Fix: uniquify shadow-variable names against the current namespace,
record the shadowed outer-scope symbol directly in a new
shadow_outer_sym field instead of deriving it from the shadow name,
and skip block-recursion into nested FORALL/DO CONCURRENT constructs
that shadow the same outer symbol.

Assisted by: Claude Sonnet 4.6

        PR fortran/125747

gcc/fortran/ChangeLog:

        * gfortran.h (gfc_forall_iterator): Add shadow_outer_sym field.
        * match.cc (apply_typespec_to_iterator): Uniquify the shadow
        variable name against the current namespace and record the
        shadowed outer-scope symbol in shadow_outer_sym.
        * resolve.cc (replace_in_code_recursive): Skip recursing into a
        nested FORALL/DO CONCURRENT body that itself shadows old_sym.
        (gfc_resolve_forall): Use shadow_outer_sym directly instead of
        deriving the shadowed symbol from the shadow variable's name.

gcc/testsuite/ChangeLog:

        * gfortran.dg/do_concurrent_typespec_2.f90: New test.
        * gfortran.dg/do_concurrent_typespec_3.f90: New test.
---
From 73e63ff5375c38f5ca01cc05d073d9d1cecf2836 Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Thu, 11 Jun 2026 12:05:15 -0700
Subject: [PATCH] fortran: [PR125747] Fix shadow-variable bugs for DO
 CONCURRENT/FORALL type-spec

When a DO CONCURRENT or FORALL construct gives its index-name an
explicit type-spec (F2018 19.4(6)) and that name already exists in the
enclosing scope, the front end creates a "shadow" variable with the
requested type and substitutes references to the outer-scope variable
with the shadow inside the construct's body.  Three related bugs in
this mechanism could cause an ICE or wrong code:

1. The shadow variable was always named "_<name>", so two sibling
   constructs reusing the same index-name with different type-specs
   ended up sharing (and mutating) the same shadow symbol, corrupting
   already-resolved bounds and triggering an ICE in
   gfc_add_modify_loc.

2. gfc_resolve_forall derived the shadowed outer-scope symbol by
   stripping a single leading underscore from the shadow variable's
   name.  Once shadow names were uniquified (fix 1) this derivation
   broke for "__name", "___name", etc., causing the body substitution
   to target the wrong symbol and produce wrong runtime values.

3. The generic block-recursion in replace_in_code_recursive
   unconditionally descended into nested FORALL/DO CONCURRENT bodies
   during an outer construct's substitution pass, prematurely
   rewriting references to the shared index-name before the nested
   construct (which shadows the same name itself) performed its own
   substitution.

Fix: uniquify shadow-variable names against the current namespace,
record the shadowed outer-scope symbol directly in a new
shadow_outer_sym field instead of deriving it from the shadow name,
and skip block-recursion into nested FORALL/DO CONCURRENT constructs
that shadow the same outer symbol.

Assisted by: Claude Sonnet 4.6

	PR fortran/125747

gcc/fortran/ChangeLog:

	* gfortran.h (gfc_forall_iterator): Add shadow_outer_sym field.
	* match.cc (apply_typespec_to_iterator): Uniquify the shadow
	variable name against the current namespace and record the
	shadowed outer-scope symbol in shadow_outer_sym.
	* resolve.cc (replace_in_code_recursive): Skip recursing into a
	nested FORALL/DO CONCURRENT body that itself shadows old_sym.
	(gfc_resolve_forall): Use shadow_outer_sym directly instead of
	deriving the shadowed symbol from the shadow variable's name.

gcc/testsuite/ChangeLog:

	* gfortran.dg/do_concurrent_typespec_2.f90: New test.
	* gfortran.dg/do_concurrent_typespec_3.f90: New test.
---
 gcc/fortran/gfortran.h                        |  2 +
 gcc/fortran/match.cc                          | 21 +++++++--
 gcc/fortran/resolve.cc                        | 46 ++++++++++---------
 .../gfortran.dg/do_concurrent_typespec_2.f90  | 32 +++++++++++++
 .../gfortran.dg/do_concurrent_typespec_3.f90  | 32 +++++++++++++
 5 files changed, 107 insertions(+), 26 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/do_concurrent_typespec_2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/do_concurrent_typespec_3.f90

diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index ef6239cd667..4c9eeda43a2 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -3179,6 +3179,8 @@ typedef struct gfc_forall_iterator
   gfc_loop_annot annot;
   /* index-name shadows a variable from outer scope.  */
   bool shadow;
+  /* The shadowed outer-scope symbol, set when SHADOW is true.  */
+  gfc_symbol *shadow_outer_sym;
   struct gfc_forall_iterator *next;
 }
 gfc_forall_iterator;
diff --git a/gcc/fortran/match.cc b/gcc/fortran/match.cc
index ef6be2d2496..507765f467a 100644
--- a/gcc/fortran/match.cc
+++ b/gcc/fortran/match.cc
@@ -2846,14 +2846,27 @@ apply_typespec_to_iterator (gfc_forall_iterator *iter, gfc_typespec *ts,
   else
     {
       /* Variable exists in outer scope - must create shadow to comply
-	 with F2018 19.4(6) scoping rules.  */
-      name = (char *) alloca (strlen (v->symtree->name) + 2);
-      strcpy (name, "_");
-      strcat (name, v->symtree->name);
+	 with F2018 19.4(6) scoping rules.  A sibling DO CONCURRENT/FORALL
+	 construct using the same index-name may already have created a
+	 "_name" shadow; pick a fresh "__name", "___name", etc. so that
+	 distinct constructs do not share (and mutate) the same shadow
+	 symbol.  */
+      int nunderscore = 1;
+      name = (char *) alloca (strlen (v->symtree->name) + 32);
+      do
+	{
+	  memset (name, '_', nunderscore);
+	  strcpy (name + nunderscore, v->symtree->name);
+	  nunderscore++;
+	}
+      while (gfc_find_symtree (gfc_current_ns->sym_root, name) != NULL);
+
       if (gfc_get_sym_tree (name, NULL, &st, false) != 0)
 	gfc_internal_error ("Failed to create shadow variable symtree for "
 			    "DO CONCURRENT type-spec at %L", loc);
 
+      iter->shadow_outer_sym = iter->var->symtree->n.sym;
+
       v = gfc_get_expr ();
       v->where = gfc_current_locus;
       v->expr_type = EXPR_VARIABLE;
diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index 8d2a0349881..c326bc4f3d1 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -12767,8 +12767,22 @@ replace_in_code_recursive (gfc_code *code, gfc_symbol *old_sym, gfc_symtree *new
 	  break;
 	}
 
-      /* Recurse into blocks */
-      if (c->block)
+      /* Recurse into blocks, unless this is a nested FORALL/DO CONCURRENT
+	 that itself shadows OLD_SYM with its own type-spec; such a nested
+	 construct replaces references to OLD_SYM in its own body with its
+	 own shadow variable, so this outer substitution must not touch
+	 them first.  */
+      bool skip_nested_shadow = false;
+      if (c->op == EXEC_FORALL || c->op == EXEC_DO_CONCURRENT)
+	for (gfc_forall_iterator *fa = c->ext.concur.forall_iterator;
+	     fa; fa = fa->next)
+	  if (fa->shadow && fa->shadow_outer_sym == old_sym)
+	    {
+	      skip_nested_shadow = true;
+	      break;
+	    }
+
+      if (c->block && !skip_nested_shadow)
 	replace_in_code_recursive (c->block->next, old_sym, new_st);
     }
 }
@@ -12863,30 +12877,18 @@ gfc_resolve_forall (gfc_code *code, gfc_namespace *ns, int forall_save)
 	{
 	  if (fa->shadow)
 	    {
-	      gfc_symtree *shadow_st;
-	      const char *shadow_name_str;
-	      char *outer_name;
+	      gfc_symtree *shadow_st = fa->var->symtree;
 
-	      /* fa->var now points to the shadow variable "_name".  */
-	      shadow_name_str = fa->var->symtree->name;
-	      shadow_st = fa->var->symtree;
+	      /* The outer-scope symbol being shadowed was recorded by
+		 apply_typespec_to_iterator; using it directly avoids
+		 deriving it from the shadow variable's name, which is
+		 not reliable once uniquified (e.g. "__i", "___i", ...)
+		 to avoid colliding with sibling constructs' shadows.  */
+	      gfc_symbol *iter_sym = fa->shadow_outer_sym;
 
-	      if (shadow_name_str[0] != '_')
-		gfc_internal_error ("Expected shadow variable name to start with _");
-
-	      outer_name = (char *) alloca (strlen (shadow_name_str));
-	      strcpy (outer_name, shadow_name_str + 1);
-
-	      /* Find the ITERATOR symbol in the current namespace.
-		 This is the local DO CONCURRENT variable that body expressions reference.  */
-	      gfc_symtree *iter_st = gfc_find_symtree (ns->sym_root, outer_name);
-
-	      if (!iter_st)
-		/* No iterator variable found - this shouldn't happen */
+	      if (!iter_sym)
 		continue;
 
-	      gfc_symbol *iter_sym = iter_st->n.sym;
-
 	      /* Walk the FORALL/DO CONCURRENT body and replace all references.  */
 	      if (code->block && code->block->next)
 		gfc_replace_forall_variable (&code->block->next, iter_sym, shadow_st);
diff --git a/gcc/testsuite/gfortran.dg/do_concurrent_typespec_2.f90 b/gcc/testsuite/gfortran.dg/do_concurrent_typespec_2.f90
new file mode 100644
index 00000000000..7a88ec3f785
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/do_concurrent_typespec_2.f90
@@ -0,0 +1,32 @@
+! { dg-do run }
+!
+! PR fortran/125747
+! Three consecutive DO CONCURRENT constructs reuse the index-name 'i'
+! with type-specs.  The second and third constructs each need a shadow
+! variable per F2018 19.4(6); they used to be assigned the same shadow
+! symbol "_i", and the third construct's INTEGER(2) type-spec mutated
+! the shadow symbol shared with the second construct, leaving its
+! already-converted INTEGER(4) bounds inconsistent with the (now
+! INTEGER(2)) loop variable and causing an ICE in gfc_add_modify_loc.
+
+program p
+  implicit none
+  integer :: s1, s2, s3
+
+  s1 = 0
+  do concurrent (integer :: i = 1:3)
+    s1 = s1 + i
+  end do
+
+  s2 = 0
+  do concurrent (integer :: i = 1:3)
+    s2 = s2 + i
+  end do
+
+  s3 = 0
+  do concurrent (integer(2) :: i = 1:3)
+    s3 = s3 + int (i, kind (s3))
+  end do
+
+  if (s1 /= 6 .or. s2 /= 6 .or. s3 /= 6) stop 1
+end program p
diff --git a/gcc/testsuite/gfortran.dg/do_concurrent_typespec_3.f90 b/gcc/testsuite/gfortran.dg/do_concurrent_typespec_3.f90
new file mode 100644
index 00000000000..16c1ee402af
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/do_concurrent_typespec_3.f90
@@ -0,0 +1,32 @@
+! { dg-do run }
+!
+! PR fortran/125747
+! A nested DO CONCURRENT that re-shadows the same index-name 'i' as its
+! enclosing construct, with a different type-spec.  The outer construct's
+! body-substitution (i -> its shadow) must not also rewrite references to
+! 'i' inside the nested construct's body, since the nested construct
+! shadows 'i' itself and performs its own substitution.
+
+program p
+  implicit none
+  integer :: s1, s2, total
+
+  ! Establish 'i' as INTEGER(4) via a prior sibling construct.
+  s1 = 0
+  do concurrent (integer :: i = 1:3)
+    s1 = s1 + i
+  end do
+
+  s2 = 0
+  total = 0
+  do concurrent (integer :: i = 1:2)
+    s2 = s2 + i
+    do concurrent (integer(2) :: i = 1:3)
+      total = total + int (i, kind (total))
+    end do
+  end do
+
+  if (s1 /= 6) stop 1
+  if (s2 /= 3) stop 2
+  if (total /= 12) stop 3
+end program p
-- 
2.54.0

Reply via email to