[Patch] C++: Handle OpenMP/OpenACC array sections [: / :] with C++26

Tobias Burnus <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
OpenMP/OpenACC supports array sections of the form:
    map ( var[start_idx : length] )
where either can be left out (the start index is then 0
and the length is taken from the type, if available.)

Hence, 'var[:n]', var[:], and 'var[3:]' might appear. While with
a space before and after the ':', there is no issue, this changes
when there is no space and C++26 is used. C++26 adds a splicer for
reflections - and uses the for slicer [: and :]. And those get now
lexed as CPP_OPEN_SPLICE and CPP_CLOSE_SPLICE; with C++23 or with
space they are lexed as CPP_OPEN/CLOSE_SQUARE and CPP_COLON.

At least currently, there should be no ambiguity as the C++26
use for splicers with reflections and OpenMP/OpenACC's use of
array sections - as the usage is orthogonal. Therefore, this patch
just handles the new tokens for the array sections.

Once committed, I intent to revert the following commit as follow up:
   r16-5330-gedc821b60ce94b   OpenMP/OpenACC tests. vs C++26

Comments, remarks, concerns before I commit the patch?ß
And before I revert r16-5330?

Tobias

PS: On the spec side, the issue has been discussed but no action taken;
on the parser-side related (and tracked in the same spec issues) is
'var[::std::max(1,5):::std::min(1,5)]'.
omp-cxx26.diff (text/x-patch, 13.1 KB)
C++: Handle OpenMP/OpenACC array sections [: / :] with C++26

OpenMP and OpenACC have array sections of the form '[ lower : size ]' where
the lower bound and/or the size bound can be left out, i.e. '[:n]', '[idx:]',
and '[:]'. However, since C++26, '[:' and ':]' are parsed as CPP_OPEN_SPLICE
and CPP_CLOSE_SPLICE, respectively - to implement the splicer as added
for reflections (see https://wg21.link/P2996).

At least as currently specified in C++ and in OpenMP/OpenACC, there should
be no ambiguity between splicing and array sections.  Therefore, this
commit just handles the new token types to process OpenMP/OpenACC array
sections as it did in C++ until C++23.

On the OpenMP specification side, this and related ':' issues are tracked
in Issue 4740 - and, on the OpenACC side, in Issue 557.

Note: This commit permits to revert the commit
  r16-5330-gedc821b60ce94b   OpenMP/OpenACC tests. vs C++26
that is planned to be done as follow up. 

The added/modified testcases ensure that also '[:' etc. is tested before
the revert - and adds some '[ :' tests for C++29 for after the revert.
After the revert, the hope is that -std= < C++26 and >= C++26 will together
span the full testspace, even if with C++26 not all OPEN_SQUARE + COLON
cases are tested for.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_postfix_expression,
	cp_parser_postfix_open_square_expression):
	cp_parser_omp_var_list_no_open): Handle OpenMP/OpenACC array
	sections that are lexed since C++26 as CPP_OPEN_SPLICE and
	CPP_CLOSE_SPLICE.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/affinity-2.c: Also run with C++ > 23
	and add some '[ :' and ': ]' tests.
	* g++.dg/gomp/allocate-3.C: Remove 'dg-skip-if' for c++26.
	* g++.dg/goacc/cache-4.C: New test.
	* g++.dg/gomp/array-section-3.C: New test.

 gcc/cp/parser.cc                             | 79 +++++++++++++++++++++++-----
 gcc/testsuite/c-c++-common/gomp/affinity-2.c | 12 ++++-
 gcc/testsuite/g++.dg/goacc/cache-4.C         | 12 +++++
 gcc/testsuite/g++.dg/gomp/allocate-3.C       |  4 --
 gcc/testsuite/g++.dg/gomp/array-section-3.C  | 16 ++++++
 5 files changed, 104 insertions(+), 19 deletions(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index a4cfcfa480e..d274320725f 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -8968,6 +8968,22 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 
       switch (token->type)
 	{
+	case CPP_OPEN_SPLICE:
+	  if (!parser->omp_array_section_p)
+	    goto default_case;
+	  /* Parse '[: length]' array section.  */
+	  postfix_expression
+	    = cp_parser_postfix_open_square_expression (parser,
+							postfix_expression,
+							false,
+							decltype_p);
+	  postfix_expression.set_range (start_loc,
+					postfix_expression.get_location ());
+
+	  idk = CP_ID_KIND_NONE;
+	  is_member_access = false;
+	  break;
+
 	case CPP_OPEN_SQUARE:
 	  if (cp_next_tokens_can_be_std_attribute_p (parser))
 	    {
@@ -9243,6 +9259,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  break;
 
 	default:
+	default_case:
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
@@ -9312,7 +9329,12 @@ cp_parser_parenthesized_expression_list_elt (cp_parser *parser, bool cast_p,
      postfix-expression [ expression-list[opt] ] (C++23)
 
    FOR_OFFSETOF is set if we're being called in that context, which
-   changes how we deal with integer constant expressions.  */
+   changes how we deal with integer constant expressions.
+
+   With parser->omp_array_section_p, it also handles OpenMP
+   array sections of the type [ index : length ] where both
+   index and length are optional. Note that an absent index
+   might be lexed as CPP_OPEN_SPLICE ('[:') since C++26.  */
 
 static tree
 cp_parser_postfix_open_square_expression (cp_parser *parser,
@@ -9326,7 +9348,9 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
   bool saved_greater_than_is_operator_p;
   bool saved_colon_corrects_to_scope_p;
 
-  /* Consume the `[' token.  */
+  bool open_splice = cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SPLICE);
+
+  /* Consume the `[' token - or with open_splice the '[:' token.  */
   cp_lexer_consume_token (parser->lexer);
 
   saved_greater_than_is_operator_p = parser->greater_than_is_operator_p;
@@ -9336,6 +9360,9 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
   if (parser->omp_array_section_p)
     parser->colon_corrects_to_scope_p = false;
 
+  if (open_splice)
+    goto post_colon_parsing;
+
   /* Parse the index expression.  */
   /* ??? For offsetof, there is a question of what to allow here.  If
      offsetof is not being used in an integral constant expression context,
@@ -9425,12 +9452,18 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 		"section");
       index = error_mark_node;
     }
+
+post_colon_parsing:
   if (parser->omp_array_section_p
-      && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+      && (open_splice
+	  || cp_lexer_next_token_is (parser->lexer, CPP_COLON)
+	  || cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SPLICE)))
     {
-      cp_lexer_consume_token (parser->lexer);
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+	cp_lexer_consume_token (parser->lexer);
       tree length = NULL_TREE;
-      if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
+      if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE)
+	  && cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SPLICE))
 	{
 	  if (cxx_dialect >= cxx23)
 	    {
@@ -9469,6 +9502,9 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 	  cp_parser_skip_to_closing_square_bracket (parser);
 	  return error_mark_node;
 	}
+      else if (!open_splice
+	       && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SPLICE))
+	cp_lexer_consume_token (parser->lexer);
       else
 	cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
@@ -41847,7 +41883,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	    case OMP_CLAUSE__CACHE_:
 	      /* The OpenACC cache directive explicitly only allows "array
 		 elements or subarrays".  */
-	      if (cp_lexer_peek_token (parser->lexer)->type != CPP_OPEN_SQUARE)
+	      if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)
+		  && !cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SPLICE))
 		{
 		  error_at (token->location, "expected %<[%>");
 		  decl = error_mark_node;
@@ -41882,25 +41919,34 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      array_section_p = false;
 	      dims.truncate (0);
-	      while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	      while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)
+		     || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SPLICE))
 		{
 		  location_t loc = UNKNOWN_LOCATION;
 		  tree low_bound = NULL_TREE, length = NULL_TREE;
 		  bool no_colon = false;
-
+		  bool open_splice = cp_lexer_next_token_is (parser->lexer,
+							     CPP_OPEN_SPLICE);
 		  parser->colon_corrects_to_scope_p = false;
 		  cp_lexer_consume_token (parser->lexer);
-		  if (!cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+		  bool close_splice = cp_lexer_next_token_is (parser->lexer,
+							     CPP_CLOSE_SPLICE);
+		  if (!open_splice
+		      && !close_splice
+		      && !cp_lexer_next_token_is (parser->lexer, CPP_COLON))
 		    {
 		      loc = cp_lexer_peek_token (parser->lexer)->location;
 		      low_bound = cp_parser_expression (parser);
 		      /* Later handling is not prepared to see through these.  */
 		      gcc_checking_assert (!location_wrapper_p (low_bound));
+		      close_splice = cp_lexer_next_token_is (parser->lexer,
+							     CPP_CLOSE_SPLICE);
 		    }
 		  if (!colon)
 		    parser->colon_corrects_to_scope_p
 		      = saved_colon_corrects_to_scope_p;
-		  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+		  if (!open_splice
+		      && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
 		    {
 		      length = integer_one_node;
 		      no_colon = true;
@@ -41908,7 +41954,9 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		  else
 		    {
 		      /* Look for `:'.  */
-		      if (!cp_parser_require (parser, CPP_COLON, RT_COLON))
+		      if (!open_splice
+			  && !close_splice
+			  && !cp_parser_require (parser, CPP_COLON, RT_COLON))
 			{
 			  if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 			      && cp_parser_simulate_error (parser))
@@ -41919,7 +41967,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 			cp_parser_commit_to_tentative_parse (parser);
 		      else
 			array_section_p = true;
-		      if (!cp_lexer_next_token_is (parser->lexer,
+		      if (!close_splice &&
+			  !cp_lexer_next_token_is (parser->lexer,
 						   CPP_CLOSE_SQUARE))
 			{
 			  length = cp_parser_expression (parser);
@@ -41928,8 +41977,10 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 			}
 		    }
 		  /* Look for the closing `]'.  */
-		  if (!cp_parser_require (parser, CPP_CLOSE_SQUARE,
-					  RT_CLOSE_SQUARE))
+		  if (close_splice)
+		    cp_lexer_consume_token (parser->lexer);
+		  else if (!cp_parser_require (parser, CPP_CLOSE_SQUARE,
+					       RT_CLOSE_SQUARE))
 		    {
 		      if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 			  && cp_parser_simulate_error (parser))
diff --git a/gcc/testsuite/c-c++-common/gomp/affinity-2.c b/gcc/testsuite/c-c++-common/gomp/affinity-2.c
index 78bd21d1a6f..9a2d678bc10 100644
--- a/gcc/testsuite/c-c++-common/gomp/affinity-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/affinity-2.c
@@ -1,4 +1,4 @@
-/* { dg-do compile { target { c || c++23_down } } } */
+/* { dg-do compile } */
 /* { dg-options "-fopenmp" } */
 
 extern int a[][10], a2[][10];
@@ -32,14 +32,24 @@ foo (int g[3][10], int h[4][8], int i[2][10], int j[][9],
     ;
   #pragma omp task affinity( b[-1:]) /* { dg-error "negative low bound in array section" } */
     ;
+  #pragma omp task affinity( b[-1: ]) /* { dg-error "negative low bound in array section" } */
+    ;
   #pragma omp task affinity( c[:-3][1:1]) /* { dg-error "negative length in array section" } */
     ;
+  #pragma omp task affinity( c[ :-3][1:1]) /* { dg-error "negative length in array section" } */
+    ;
+  #pragma omp task affinity( d[11: ]) /* { dg-error "low bound \[^\n\r]* above array section size" } */
+    ;
   #pragma omp task affinity( d[11:]) /* { dg-error "low bound \[^\n\r]* above array section size" } */
     ;
   #pragma omp task affinity( e[:11]) /* { dg-error "length \[^\n\r]* above array section size" } */
     ;
   #pragma omp task affinity( f[1:10]) /* { dg-error "high bound \[^\n\r]* above array section size" } */
     ;
+  #pragma omp task affinity( g[: ][2:4]) /* { dg-error "for array function parameter length expression must be specified" } */
+    ;
+  #pragma omp task affinity( g[ :][2:4]) /* { dg-error "for array function parameter length expression must be specified" } */
+    ;
   #pragma omp task affinity( g[:][2:4]) /* { dg-error "for array function parameter length expression must be specified" } */
     ;
   #pragma omp task affinity( h[2:2][-1:]) /* { dg-error "negative low bound in array section" } */
diff --git a/gcc/testsuite/g++.dg/goacc/cache-4.C b/gcc/testsuite/g++.dg/goacc/cache-4.C
new file mode 100644
index 00000000000..9c6ca229501
--- /dev/null
+++ b/gcc/testsuite/g++.dg/goacc/cache-4.C
@@ -0,0 +1,12 @@
+int l[10];
+
+void
+foo ()
+{
+  #pragma acc cache(l) /* { dg-error "expected '\\\['" } */
+    ;
+  #pragma acc cache(l[:7.5f]) /* { dg-error "length \[^\n\r]* of array section does not have integral type" } */
+    ;
+  #pragma acc cache(l[ :7.5f]) /* { dg-error "length \[^\n\r]* of array section does not have integral type" } */
+    ;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/allocate-3.C b/gcc/testsuite/g++.dg/gomp/allocate-3.C
index 0303b55fa25..e778314f07e 100644
--- a/gcc/testsuite/g++.dg/gomp/allocate-3.C
+++ b/gcc/testsuite/g++.dg/gomp/allocate-3.C
@@ -1,7 +1,3 @@
-// Array sections without spaces between [ and : or : and ] are incompatible
-// with C++26.
-// { dg-skip-if "array sections vs. C++26" { c++26 } }
-
 template <typename T>
 void
 foo (T &x, T (&y)[4], T *&z, int &u, int (&v)[4], int *&w)
diff --git a/gcc/testsuite/g++.dg/gomp/array-section-3.C b/gcc/testsuite/g++.dg/gomp/array-section-3.C
new file mode 100644
index 00000000000..784d6632422
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-section-3.C
@@ -0,0 +1,16 @@
+// { dg-additional-options "-fdump-tree-original" }
+
+// Check that OpenMP array sections with [: and :] do not get mixed up with C++26's splice specifier,
+// used for reflection.
+//
+// See also OpenMP spec Issue 4740.
+
+void f() {
+ char a1[4], a2[4], a3[4], b[5];
+ char c1[4], c2[4], c3[4];
+
+ #pragma omp target map(to: a1[:], a2[:2], a3[2:], b[1:2], c1[ : ], c2[ : 2], c3[2: ])
+   ;
+}
+
+// { dg-final { scan-tree-dump "#pragma omp target.* map\\(to:c3\\\[2\\\] \\\[len: 2\\\]\\).* map\\(to:c2\\\[0\\\] \\\[len: 2\\\]\\).* map\\(to:c1\\\[0\\\] \\\[len: 4\\\]\\).* map\\(to:b\\\[1\\\] \\\[len: 2\\\]\\).* map\\(to:a3\\\[2\\\] \\\[len: 2\\\]\\).* map\\(to:a2\\\[0\\\] \\\[len: 2\\\]\\).* map\\(to:a1\\\[0\\\] \\\[len: 4\\\]\\)" "original" } }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.