Hi Tobias,

On 14/09/2026 12:13, Tobias Burnus wrote:
Hi PA,

thanks for the update.

Paul-Antoine Arras wrote:
I added usm_env_handling-4.c and usm_env_handling-4-self.c. Both pass on my local GPU (nvptx). However, on gfx90a, all usm_env_handling-?.c tests (the ones setting GOMP_RUNTIME_USM "enabled") show up as unsupported. Is this expected?

[This was seemingly due DejaGNU not supporting dg-set-target-env-var with
remote testing; ]

I ran the tests manually, all pass.

Anyway, this was good to add this test as it uncovered missing conditions for GOMP_MAP_TO_GRID and GOMP_MAP_FROM_GRID in scan_sharing_clauses.

:-)

* * *

LGTM.

Thanks,

Tobias

PS: 'interdiff' looks scary, until one realizes that it failed half-way through. directly comparing works (graphical 'diff'), but the difference also contains all
the post 1/3 changes that were reviewed before.

(Hopefully, this will get better when we switch to the forge.)

* * *

While going through the Fortran review, I noticed that there was a case working in C and not in C++:

#pragma omp target update to(arr[::2])

The literal 2 would get rejected because it was treated as an id-expression. This is now fixed in 0001-WIP-C-fixup-for-non-id-expr.patch.

* * *

Something that also came up as part of the Fortran review, but actually belongs in the middle end, is the lack of a positive check for a stride that is not known at compile time. This check is now added in omp-low.cc and fires at run time whenever the stride is not a compile-time constant. See 0002-ME-fixup-to-check-at-runtime-for-non-positive-stride.patch.

Thanks,
--
PA
From 4f093fe9b4aa3083995b66de263cc29524961315 Mon Sep 17 00:00:00 2001
From: Paul-Antoine Arras <[email protected]>
Date: Wed, 16 Sep 2026 17:23:17 +0200
Subject: [PATCH 1/2] WIP C++ fixup for '[::<non-id-expr>'

---
 gcc/cp/parser.cc                              | 33 ++++++++++++++-----
 .../c-c++-common/gomp/array-section-1.c       |  4 +--
 gcc/testsuite/g++.dg/gomp/array-section-7.C   | 18 ++++++++++
 3 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 58e6810b710..b16ff026d55 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -9378,6 +9378,22 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
   bool double_colon_only
     = cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)
       && cp_lexer_nth_token_is (parser->lexer, 2, CPP_CLOSE_SQUARE);
+  bool triple_colon = cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)
+		      && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON);
+
+  bool double_colon_stride = false;
+  if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
+      && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE) && !double_scope
+      && !double_colon_only && !triple_colon)
+    {
+      cp_parser_parse_tentatively (parser);
+      cp_parser_expression (parser, NULL, /*cast_p=*/false,
+			    /*decltype_p=*/false,
+			    /*warn_comma_p=*/warn_comma_subscript);
+      if (cp_parser_error_occurred (parser))
+	double_colon_stride = true;
+      cp_parser_abort_tentative_parse (parser);
+    }
 
   saved_greater_than_is_operator_p = parser->greater_than_is_operator_p;
   parser->greater_than_is_operator_p = true;
@@ -9400,9 +9416,9 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
   if (for_offsetof)
     index = cp_parser_constant_expression (parser);
   else if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
-	   && (double_scope || double_colon_only))
+	   && (double_scope || double_colon_only || double_colon_stride))
     {
-      /* Handle '[::::' and '[::]' in post_colon_parsing.  */
+      /* Handle '[::::', '[::]' and '[::<non-id-expr>' in post_colon_parsing. */
     }
   else if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_NONE
 	   || (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)
@@ -9467,8 +9483,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 	  maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
 	  index = cp_parser_braced_list (parser);
 	}
-      else if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)
-	       && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+      else if (triple_colon)
 	{
 	  /* The lexer greedily tokenizes '[:::' as '[ :: :'.
 	     It should be '[ : ::'. Let's swap the last two.  */
@@ -9504,12 +9519,12 @@ post_colon_parsing:
       && (open_splice || cp_lexer_next_token_is (parser->lexer, CPP_COLON)
 	  || cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SPLICE)
 	  || (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
-	      && (double_scope || double_colon_only))))
+	      && (double_scope || double_colon_only || double_colon_stride))))
     {
       tree length = NULL_TREE, stride = NULL_TREE;
       bool close_splice = cp_lexer_next_token_is (parser->lexer,
 						  CPP_CLOSE_SPLICE);
-      if (!open_splice && !double_scope)
+      if (!open_splice && !double_scope && !double_colon_stride)
 	cp_lexer_consume_token (parser->lexer);
       if (open_splice && close_splice)
 	{
@@ -9527,7 +9542,7 @@ post_colon_parsing:
 	  cp_lexer_peek_nth_token (parser->lexer, 2)->type = CPP_SCOPE;
 	}
       else if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE)
-	       && !close_splice && !double_scope
+	       && !close_splice && !double_scope && !double_colon_stride
 	       && cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
 	{
 	  if (cxx_dialect >= cxx23)
@@ -9561,8 +9576,8 @@ post_colon_parsing:
 	}
 
       if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
-	  && (cp_lexer_next_token_is (parser->lexer, CPP_COLON)
-	      || double_scope))
+	  && (cp_lexer_next_token_is (parser->lexer, CPP_COLON) || double_scope
+	      || double_colon_stride))
 	{
 	  cp_lexer_consume_token (parser->lexer);
 	  /* We could check for C++-23 multidimensional/comma-separated
diff --git a/gcc/testsuite/c-c++-common/gomp/array-section-1.c b/gcc/testsuite/c-c++-common/gomp/array-section-1.c
index c0561284ac4..5af3f950dd7 100644
--- a/gcc/testsuite/c-c++-common/gomp/array-section-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/array-section-1.c
@@ -1,14 +1,12 @@
 /* { dg-do compile } */
 
-/* Check that array section syntax with empty length is handled correctly.
-   '::1' is valid only in C.  */
+/* Check that array section syntax with empty length is handled correctly.  */
 
 int n;
 
 void f() {
    int A[10], B[10][10];
 #pragma omp target update to(A[::], B[::][::1])
-/* { dg-error "expected id-expression before numeric constant" "" { target c++ } .-1 } */
 
 #pragma omp target update to(A[::], B[0][: : ])
 #pragma omp target update to(A[1:2:], B[:1:][1: : ])
diff --git a/gcc/testsuite/g++.dg/gomp/array-section-7.C b/gcc/testsuite/g++.dg/gomp/array-section-7.C
index 5dc87030c08..9fb282bfde4 100644
--- a/gcc/testsuite/g++.dg/gomp/array-section-7.C
+++ b/gcc/testsuite/g++.dg/gomp/array-section-7.C
@@ -3,6 +3,9 @@
 
 // Check parsing of double colon (::). In case of ambiguity between the scope
 // resolution operator and the array section syntax, spaces are required.
+// There is only an actual ambiguity when what follows "::" could itself be
+// a qualified-id (e.g. an identifier); for anything else (a literal, a
+// parenthesized expression, ...) no space is needed.
 
 int lb2 = 4;
 int len2 = 16;
@@ -29,6 +32,21 @@ void g() {
 #pragma omp target update to(arr[::])
 // { dg-final { scan-tree-dump "to\\(arr\\\[0\\\] \\\[len: 512\\\]\\)" "original" } }
 
+  // Valid: no ambiguity, since a numeric literal can never start an
+  // id-expression, so "2" is unambiguously the stride.
+#pragma omp target update to(arr[::2])
+// { dg-final { scan-tree-dump "map\\(to_grid:arr \\\[len: 4\\\]\\) map\\(grid_dim:0 \\\[len: 64\\\]\\) map\\(grid_stride:2\\)" "original" } }
+
+  // Likewise for a parenthesized expression as the stride.
+#pragma omp target update to(arr[::(s)])
+// { dg-final { scan-tree-dump "map\\(to_grid:arr \\\[len: 4\\\]\\) map\\(grid_dim:0 \\\[len: 128 /\\\[cl\\\] \\(sizetype\\) s\\\]\\) map\\(grid_stride:s\\)" "original" } }
+
+  // A negative or zero stride is rejected as a semantic error (not a parse
+  // failure), same as when written with a disambiguating space.
+#pragma omp target update to(arr[::-2])
+// { dg-error "negative or zero stride" "" { target *-*-* } .-1 }
+// { dg-error "must contain at least one" "" { target *-*-* } .-2 }
+
   // Valid: len2 is looked up in the global namespace and forms the length of the
   // array section.
 #pragma omp target update to(arr[:::len2])
-- 
2.55.0

From 0cbafd6f8adf044fd93b665552fd15849efa5594 Mon Sep 17 00:00:00 2001
From: Paul-Antoine Arras <[email protected]>
Date: Wed, 16 Sep 2026 20:15:22 +0200
Subject: [PATCH 2/2] ME fixup to check at runtime for non-positive stride

---
 gcc/omp-low.cc                                | 45 +++++++++++++++++++
 .../target-update-negative-stride-1.c         | 26 +++++++++++
 2 files changed, 71 insertions(+)
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-update-negative-stride-1.c

diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index 383eb8dd9c1..819c3a0b149 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -13738,6 +13738,51 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 				== GOMP_MAP_GRID_STRIDE))
 			  {
 			    stride = OMP_CLAUSE_DECL (nc2);
+
+			    if (TREE_CODE (stride) != INTEGER_CST)
+			      {
+				/* Check at run time that stride is positive. */
+				location_t loc = gimple_location_safe (ilist);
+				tree err
+				  = builtin_decl_explicit (BUILT_IN_GOMP_ERROR);
+				const char *str
+				  = "Non-positive stride in array section";
+				tree msg
+				  = build_string_literal (strlen (str) + 1,
+							  str);
+				tree msglen
+				  = build_int_cst (size_type_node, -2);
+				err = build_call_expr_loc (loc, err, 2, msg,
+							   msglen);
+				tree l1
+				  = create_artificial_label (UNKNOWN_LOCATION);
+				tree l2
+				  = create_artificial_label (UNKNOWN_LOCATION);
+				tree l3
+				  = create_artificial_label (UNKNOWN_LOCATION);
+
+				gimple_seq new_body = NULL;
+
+				gcond *cond = gimple_build_cond (
+				  LE_EXPR, stride,
+				  build_int_cst (TREE_TYPE (stride), 0), l1,
+				  l2);
+				gimple_seq_add_stmt (&new_body, cond);
+				gimple_seq_add_stmt (&new_body,
+						     gimple_build_label (l1));
+				gimplify_and_add (err, &new_body);
+				gimple_seq_add_stmt (&new_body,
+						     gimple_build_goto (l3));
+				gimple_seq_add_stmt (&new_body,
+						     gimple_build_label (l2));
+				gimple_seq_add_seq (&new_body, ilist);
+				gimple_seq_add_stmt (&new_body,
+						     gimple_build_label (l3));
+				ilist = gimple_build_bind (NULL_TREE, new_body,
+							   make_node (BLOCK));
+				gimple_set_location (ilist, loc);
+			      }
+
 			    stride = fold_convert (sizetype, stride);
 			    if (OMP_CLAUSE_SIZE (nc2))
 			      {
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-update-negative-stride-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-update-negative-stride-1.c
new file mode 100644
index 00000000000..aeb0ff1ce54
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-update-negative-stride-1.c
@@ -0,0 +1,26 @@
+/* { dg-shouldfail "non-positive stride in array section" } */
+
+/* A stride that is only known to be non-positive at run time (as opposed to
+   a literal, which is rejected at compile time already) must still be
+   rejected, not silently ignored.  */
+
+int
+main (void)
+{
+  int n = 20;
+  int arr[20];
+
+  for (int i = 0; i < n; i++)
+    arr[i] = i;
+
+  int stride = -2;
+
+  #pragma omp target data map(tofrom: arr[0:20])
+  {
+    #pragma omp target update from(arr[0:10:stride])
+  }
+
+  return 0;
+}
+
+/* { dg-output "libgomp: fatal error: Non-positive stride in array section" } */
-- 
2.55.0

Reply via email to