Here is a follow-up patch to the C++ front end that handles ambiguous
syntaxes implying colons in array sections with strides, including the
scope and splice notations.
--
PA
From c628cdfbcea159549d0349bf07abb49fae458fd2 Mon Sep 17 00:00:00 2001
From: Paul-Antoine Arras <[email protected]>
Date: Thu, 6 Aug 2026 20:03:30 +0200
Subject: [PATCH] C++: Disambiguate colon syntaxes in array sections
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The general syntax for array sections is: '[index:length:stride]'; any of the
three components may be omitted. Runs of adjacent colons in this syntax (e.g.
'[::', '[:::', '[::::', trailing '::]') can ambiguously refer to the splice
([:/:]) and scope (::) notations, as well as component separators of the array
section.
While awaiting clarification from the OpenMP committe, this patch adopts the
following stance: "In an intra-clause-specification, if a sequence of two or
more colons appears without separating whitespace, they are treated as follows:
the trailing :: is treated as nested-name-specifier or being part of a
nested-name-specifier – the other colons, if any, as separators according to the
OpenMP syntax."
gcc/cp/ChangeLog:
* parser.cc (cp_parser_new): Rename omp_array_section_p to
omp_array_section_kind.
(cp_parser_statement_expr): Likewise.
(cp_parser_postfix_expression): Likewise.
(cp_parser_postfix_open_square_expression): Support scope (::) and
splice ([:/:]) syntaxes in combination with strides in array sections.
(cp_parser_parenthesized_expression_list): Rename
omp_array_section_p to omp_array_section_kind.
(cp_parser_cast_expression): Likewise.
(cp_parser_lambda_expression): Likewise.
(cp_parser_braced_list): Likewise.
(cp_parser_skip_up_to_closing_square_bracket): Likewise.
(cp_parser_omp_var_list_no_open): Pass
OMP_ARRAY_SECTION_STRIDED for 'to'/'from' clauses and
OMP_ARRAY_SECTION_UNSTRIDED for 'map' clauses, instead of a
single boolean.
* parser.h (omp_array_section_p): Replace bool with...
(omp_array_section_kind): ...this unsigned char.
(OMP_ARRAY_SECTION_NONE, OMP_ARRAY_SECTION_UNSTRIDED)
(OMP_ARRAY_SECTION_STRIDED): New macros.
gcc/testsuite/ChangeLog:
* g++.dg/gomp/array-section-7.C: New test.
* g++.dg/gomp/array-section-8.C: New test.
---
gcc/cp/parser.cc | 116 +++++++++++++-------
gcc/cp/parser.h | 6 +-
gcc/testsuite/g++.dg/gomp/array-section-7.C | 36 ++++++
gcc/testsuite/g++.dg/gomp/array-section-8.C | 57 ++++++++++
4 files changed, 173 insertions(+), 42 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/gomp/array-section-7.C
create mode 100644 gcc/testsuite/g++.dg/gomp/array-section-8.C
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 2d5b3c0ed3c..ab672aed649 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -4777,7 +4777,7 @@ cp_parser_new (cp_lexer *lexer)
parser->oacc_routine = NULL;
/* Disallow OpenMP array sections in expressions. */
- parser->omp_array_section_p = false;
+ parser->omp_array_section_kind = false;
/* Disallow OpenMP array-shaping operator in expressions. */
parser->omp_array_shaping_op_p = false;
@@ -5815,7 +5815,7 @@ static cp_expr
cp_parser_statement_expr (cp_parser *parser)
{
cp_token_position start = cp_parser_start_tentative_firewall (parser);
- auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto oas = make_temp_override (parser->omp_array_section_kind, false);
auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
/* Consume the '('. */
@@ -8926,9 +8926,9 @@ 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)
+ if (!parser->omp_array_section_kind)
goto default_case;
- /* Parse '[: length]' array section. */
+ /* Parse '[: length :]' array section. */
postfix_expression
= cp_parser_postfix_open_square_expression (parser,
postfix_expression,
@@ -9289,9 +9289,10 @@ cp_parser_parenthesized_expression_list_elt (cp_parser *parser, bool cast_p,
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. */
+ array sections of the type [ index : length : stride ] where index,
+ length and stride are optional. Note that an absent index
+ might be lexed as CPP_OPEN_SPLICE ('[:') and an absent stride
+ might be lexed as CPP_CLOSE_SPLICE (':]') since C++26. */
static tree
cp_parser_postfix_open_square_expression (cp_parser *parser,
@@ -9310,11 +9311,17 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
/* Consume the `[' token - or with open_splice the '[:' token. */
cp_lexer_consume_token (parser->lexer);
+ bool double_scope = cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_SCOPE);
+ bool double_colon_only
+ = cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_CLOSE_SQUARE);
+
saved_greater_than_is_operator_p = parser->greater_than_is_operator_p;
parser->greater_than_is_operator_p = true;
saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
- if (parser->omp_array_section_p)
+ if (parser->omp_array_section_kind)
parser->colon_corrects_to_scope_p = false;
if (open_splice)
@@ -9330,7 +9337,12 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
constant expressions here. */
if (for_offsetof)
index = cp_parser_constant_expression (parser);
- else if (!parser->omp_array_section_p
+ else if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
+ && (double_scope || double_colon_only))
+ {
+ /* Handle '[::::' and '[::]' in post_colon_parsing. */
+ }
+ else if (!parser->omp_array_section_kind
|| (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)
&& cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SPLICE)))
{
@@ -9393,6 +9405,14 @@ 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))
+ {
+ /* The lexer greedily tokenizes '[:::' as '[ :: :'.
+ It should be '[ : ::'. Let's swap the last two. */
+ cp_lexer_peek_token (parser->lexer)->type = CPP_COLON;
+ cp_lexer_peek_nth_token (parser->lexer, 2)->type = CPP_SCOPE;
+ }
else
index = cp_parser_expression (parser, NULL, /*cast_p=*/false,
/*decltype_p=*/false,
@@ -9402,8 +9422,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
post_colon_parsing:
parser->greater_than_is_operator_p = saved_greater_than_is_operator_p;
- if (cxx_dialect >= cxx23
- && parser->omp_array_section_p
+ if (cxx_dialect >= cxx23 && parser->omp_array_section_kind
&& expression_list.get () != NULL
&& vec_safe_length (expression_list) > 1)
{
@@ -9412,15 +9431,16 @@ post_colon_parsing:
index = error_mark_node;
}
- if (parser->omp_array_section_p
- && (open_splice
- || cp_lexer_next_token_is (parser->lexer, CPP_COLON)
- || cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SPLICE)))
+ if (parser->omp_array_section_kind
+ && (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))))
{
tree length = NULL_TREE, stride = NULL_TREE;
bool close_splice = cp_lexer_next_token_is (parser->lexer,
CPP_CLOSE_SPLICE);
- if (!open_splice)
+ if (!open_splice && !double_scope)
cp_lexer_consume_token (parser->lexer);
if (open_splice && close_splice)
{
@@ -9428,19 +9448,24 @@ post_colon_parsing:
UNKNOWN_LOCATION);
length = error_mark_node;
}
+ else if (open_splice && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ {
+ /* The lexer greedily tokenizes '[::::' as '[: :: :'.
+ It should be '[ : : ::'. Let's swap the last two. */
+ cp_lexer_peek_token (parser->lexer)->type = CPP_COLON;
+ 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)
+ && !close_splice && !double_scope)
{
if (cxx_dialect >= cxx23)
{
- cp_expr expr
- = cp_parser_parenthesized_expression_list_elt (parser,
- /*cast_p=*/
- false,
- /*allow_exp_p=*/
- true,
- /*non_cst_p=*/
- NULL);
+ cp_expr expr = cp_parser_parenthesized_expression_list_elt (
+ parser,
+ /*cast_p=*/false,
+ /*allow_exp_p=*/true,
+ /*non_cst_p=*/NULL);
if (expr == error_mark_node)
length = error_mark_node;
@@ -9450,7 +9475,7 @@ post_colon_parsing:
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
{
error_at (loc, "cannot use multidimensional subscript in "
- "OpenMP array section");
+ "OpenMP array section");
length = error_mark_node;
}
}
@@ -9461,7 +9486,9 @@ post_colon_parsing:
/*warn_comma_p=*/warn_comma_subscript);
}
- if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
+ && (cp_lexer_next_token_is (parser->lexer, CPP_COLON)
+ || double_scope))
{
cp_lexer_consume_token (parser->lexer);
/* We could check for C++-23 multidimensional/comma-separated
@@ -9482,7 +9509,13 @@ post_colon_parsing:
cp_parser_skip_to_closing_square_bracket (parser);
return error_mark_node;
}
- else if (!close_splice)
+ else if (parser->omp_array_section_kind == OMP_ARRAY_SECTION_STRIDED
+ && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SPLICE))
+ cp_lexer_consume_token (parser->lexer);
+ else if (!close_splice
+ && (OMP_ARRAY_SECTION_UNSTRIDED
+ || cp_lexer_next_token_is_not (parser->lexer,
+ CPP_CLOSE_SPLICE)))
cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
return grok_omp_array_section (input_location, postfix_expression, index,
@@ -9888,9 +9921,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
= parser->greater_than_is_operator_p;
parser->greater_than_is_operator_p = true;
- saved_omp_array_section_p = parser->omp_array_section_p;
+ saved_omp_array_section_p = parser->omp_array_section_kind;
saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
- parser->omp_array_section_p = false;
+ parser->omp_array_section_kind = false;
parser->omp_array_shaping_op_p = false;
cp_expr expr (NULL_TREE);
@@ -9985,7 +10018,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
{
parser->greater_than_is_operator_p
= saved_greater_than_is_operator_p;
- parser->omp_array_section_p = saved_omp_array_section_p;
+ parser->omp_array_section_kind = saved_omp_array_section_p;
parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
return NULL;
}
@@ -9993,7 +10026,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
parser->greater_than_is_operator_p
= saved_greater_than_is_operator_p;
- parser->omp_array_section_p = saved_omp_array_section_p;
+ parser->omp_array_section_kind = saved_omp_array_section_p;
parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
return expression_list;
@@ -11542,7 +11575,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
else if (parser->omp_array_shaping_op_p
&& cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
{
- auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto oas = make_temp_override (parser->omp_array_section_kind, false);
auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
@@ -12929,7 +12962,7 @@ cp_parser_lambda_expression (cp_parser* parser,
cp_binding_level* implicit_template_scope = parser->implicit_template_scope;
bool auto_is_implicit_function_template_parm_p
= parser->auto_is_implicit_function_template_parm_p;
- bool saved_omp_array_section_p = parser->omp_array_section_p;
+ bool saved_omp_array_section_p = parser->omp_array_section_kind;
bool saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
bool saved_in_targ = parser->in_template_argument_list_p;
bool saved_in_declarator_p = parser->in_declarator_p;
@@ -12943,7 +12976,7 @@ cp_parser_lambda_expression (cp_parser* parser,
parser->implicit_template_parms = 0;
parser->implicit_template_scope = 0;
parser->auto_is_implicit_function_template_parm_p = false;
- parser->omp_array_section_p = false;
+ parser->omp_array_section_kind = false;
parser->omp_array_shaping_op_p = false;
parser->in_template_argument_list_p = false;
parser->in_declarator_p = false;
@@ -13012,7 +13045,7 @@ cp_parser_lambda_expression (cp_parser* parser,
parser->implicit_template_scope = implicit_template_scope;
parser->auto_is_implicit_function_template_parm_p
= auto_is_implicit_function_template_parm_p;
- parser->omp_array_section_p = saved_omp_array_section_p;
+ parser->omp_array_section_kind = saved_omp_array_section_p;
parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
parser->in_template_argument_list_p = saved_in_targ;
parser->in_declarator_p = saved_in_declarator_p;
@@ -29194,7 +29227,7 @@ cp_parser_braced_list (cp_parser *parser, bool *non_constant_p /*=nullptr*/)
{
tree initializer;
location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
- auto oas = make_temp_override (parser->omp_array_section_p, false);
+ auto oas = make_temp_override (parser->omp_array_section_kind, false);
auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
/* Within a brace-enclosed initializer list, a `>' token is always the
@@ -29271,7 +29304,7 @@ cp_parser_skip_up_to_closing_square_bracket (cp_parser *parser)
return false;
case CPP_OPEN_SPLICE:
- if (!parser->omp_array_section_p)
+ if (!parser->omp_array_section_kind)
break;
/* FALLTHRU */
@@ -29280,7 +29313,7 @@ cp_parser_skip_up_to_closing_square_bracket (cp_parser *parser)
break;
case CPP_CLOSE_SPLICE:
- if (!parser->omp_array_section_p)
+ if (!parser->omp_array_section_kind)
break;
/* FALLTHRU */
case CPP_CLOSE_SQUARE:
@@ -41680,7 +41713,10 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
|| kind == OMP_CLAUSE_TO
|| kind == OMP_CLAUSE_FROM))
{
- auto s = make_temp_override (parser->omp_array_section_p, true);
+ auto s = make_temp_override (parser->omp_array_section_kind,
+ kind == OMP_CLAUSE_MAP
+ ? OMP_ARRAY_SECTION_UNSTRIDED
+ : OMP_ARRAY_SECTION_STRIDED);
auto o = make_temp_override (parser->omp_array_shaping_op_p,
(kind == OMP_CLAUSE_TO
|| kind == OMP_CLAUSE_FROM));
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 2f696502246..19dc0154372 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -425,8 +425,10 @@ struct GTY(()) cp_parser {
appear. */
bool omp_attrs_forbidden_p;
- /* TRUE if an OpenMP array section is allowed. */
- bool omp_array_section_p;
+#define OMP_ARRAY_SECTION_NONE 0
+#define OMP_ARRAY_SECTION_UNSTRIDED 1
+#define OMP_ARRAY_SECTION_STRIDED 2
+ unsigned char omp_array_section_kind;
/* TRUE if an OpenMP array-shaping operator is allowed. */
bool omp_array_shaping_op_p;
diff --git a/gcc/testsuite/g++.dg/gomp/array-section-7.C b/gcc/testsuite/g++.dg/gomp/array-section-7.C
new file mode 100644
index 00000000000..622e100be29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-section-7.C
@@ -0,0 +1,36 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection -fdump-tree-original" }
+
+// Check that the OpenMP stride array-section notation
+// '[ lower : length : stride ]' is not confused with C++26's splice specifier,
+// including when the latter is used to supply one of the section's components.
+
+constexpr int lb0 = 4;
+constexpr auto rlb = ^^lb0;
+constexpr int s0 = 2;
+constexpr auto rs = ^^s0;
+
+void f() {
+ int arr[128];
+ int lb = 4, len = 16, s = 2;
+
+ // baseline: all three components explicit, nothing splice-shaped.
+#pragma omp target update to(arr[lb:len:s])
+
+ // splice-expression supplying the lower bound.
+#pragma omp target update to(arr[[:rlb:]:len:s])
+
+ // splice-expression supplying the stride.
+#pragma omp target update to(arr[lb:len:[:rs:]])
+
+ // Stride left empty: the trailing ':' + ']' is lexed as a single
+ // CPP_CLOSE_SPLICE token.
+#pragma omp target update to(arr[lb:len:])
+#pragma omp target update to(arr[:len:])
+}
+
+// { dg-final { scan-tree-dump "map\\(to_grid:arr \\\[len: 4\\\]\\) map\\(grid_dim:SAVE_EXPR <lb> \\\[len: len\\\]\\) map\\(grid_stride:s\\)" "original" } }
+// { dg-final { scan-tree-dump "map\\(to_grid:arr \\\[len: 4\\\]\\) map\\(grid_dim:4 \\\[len: len\\\]\\) map\\(grid_stride:s\\)" "original" } }
+// { dg-final { scan-tree-dump "map\\(to_grid:arr \\\[len: 4\\\]\\) map\\(grid_dim:SAVE_EXPR <lb> \\\[len: len\\\]\\) map\\(grid_stride:2\\)" "original" } }
+// { dg-final { scan-tree-dump "to\\(arr\\\[SAVE_EXPR <lb>\\\] \\\[len: \\(sizetype\\) len \\* 4\\\]\\)" "original" } }
+// { dg-final { scan-tree-dump "to\\(arr\\\[0\\\] \\\[len: \\(sizetype\\) len \\* 4\\\]\\)" "original" } }
diff --git a/gcc/testsuite/g++.dg/gomp/array-section-8.C b/gcc/testsuite/g++.dg/gomp/array-section-8.C
new file mode 100644
index 00000000000..08f07e000de
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-section-8.C
@@ -0,0 +1,57 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-original" }
+
+// Check parsing of double colon (::). In case of ambiguity between the scope
+// resolution operator and the array section syntax, spaces are required.
+
+int lb2 = 4;
+int len2 = 16;
+int s2 = 2;
+
+void g() {
+ int arr[128];
+ int lb = 4, len = 16, s = 2;
+
+#pragma omp target update to(arr[lb::s])
+// { dg-error "34: 'lb' is not a class" "" { target *-*-* } .-1 }
+// { dg-error "must contain at least one" "" { target *-*-* } .-2 }
+
+#pragma omp target update to(arr[::s])
+// { dg-error "36: '::s' has not been declared" "" { target *-*-* } .-1 }
+// { dg-error "must contain at least one" "" { target *-*-* } .-2 }
+
+#pragma omp target update to(arr[lb::])
+// { dg-error "34: 'lb' is not a class" "" { target *-*-* } .-1 }
+// { dg-error "38: expected unqualified-id before '\\\]' token" "" { target *-*-* } .-2 }
+// { dg-error "must contain at least one" "" { target *-*-* } .-3 }
+
+ // Valid: default values.
+#pragma omp target update to(arr[::])
+// { dg-final { scan-tree-dump "to\\(arr\\\[0\\\] \\\[len: 512\\\]\\)" "original" } }
+
+ // Valid: len2 is looked up in the global namespace and forms the length of the
+ // array section.
+#pragma omp target update to(arr[:::len2])
+#pragma omp target update to(arr[:::len2:])
+// { dg-final { scan-tree-dump-times "to\\(arr\\\[0\\\] \\\[len: \\(sizetype\\) len2 \\* 4\\\]\\)" 2 "original" } }
+
+ // Valid: s2 is looked up in the global namespace and forms the stride of the
+ // array section.
+#pragma omp target update to(arr[::::s2])
+// { dg-final { scan-tree-dump "map\\(to_grid:arr \\\[len: 4\\\]\\) map\\(grid_dim:0 \\\[len: 1\\\]\\) map\\(grid_stride:s2\\)" "original" } }
+
+ // Valid: lb2 is looked up in the global namespace and forms the lower bound
+ // of the array section.
+#pragma omp target update to(arr[::lb2:])
+// { dg-final { scan-tree-dump "to\\(arr\\\[SAVE_EXPR <lb2>\\\] \\\[len: \\(128 - \\(sizetype\\) SAVE_EXPR <lb2>\\) \\* 4\\\]\\)" "original" } }
+
+#pragma omp target update to(arr[::lb2::])
+// { dg-error "36: '::lb2' is not a class" "" { target *-*-* } .-1 }
+// { dg-error "41: expected unqualified-id before '\\\]' token" "" { target *-*-* } .-2 }
+// { dg-error "must contain at least one" "" { target *-*-* } .-3 }
+
+#pragma omp target update to(arr[::lb2::s2])
+// { dg-error "36: '::lb2' is not a class" "" { target *-*-* } .-1 }
+// { dg-error "must contain at least one" "" { target *-*-* } .-2 }
+
+}
--
2.53.0