Re: [PATCH 2/5] openmp: Support pointer indirection in array-shaping casts and array sections
Paul-Antoine Arras <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
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
0001-C-Disambiguate-colon-syntaxes-in-array-sections.patch
(text/x-patch, 20.2 KB)
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