[5/7] openmp: Add C++ support for parsing metadirectives
Commit Message
This patch adds metadirective parsing support to the C++ parser. This is
basically just a straight port of the C code to the C++ front end.
Kwok
From e9bb138d4c3f560e48e408facce2361533685a98 Mon Sep 17 00:00:00 2001
From: Kwok Cheung Yeung <kcy@codesourcery.com>
Date: Mon, 6 Dec 2021 22:58:01 +0000
Subject: [PATCH 5/7] openmp: Add C++ support for parsing metadirectives
This adds support for parsing OpenMP metadirectives in the C++ front end.
2021-12-10 Kwok Cheung Yeung <kcy@codesourcery.com>
gcc/cp/
* parser.c (cp_parser_skip_to_end_of_statement): Handle parentheses.
(cp_parser_skip_to_end_of_block_or_statement): Likewise.
(cp_parser_omp_context_selector): Add extra argument. Allow
non-constant expressions.
(cp_parser_omp_context_selector_specification): Add extra argument and
propagate to cp_parser_omp_context_selector.
(analyze_metadirective_body): New.
(cp_parser_omp_metadirective): New.
(cp_parser_omp_construct): Handle PRAGMA_OMP_METADIRECTIVE.
(cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE.
---
gcc/cp/parser.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 417 insertions(+), 8 deletions(-)
Comments
On Fri, Dec 10, 2021 at 05:37:34PM +0000, Kwok Cheung Yeung wrote:
> From e9bb138d4c3f560e48e408facce2361533685a98 Mon Sep 17 00:00:00 2001
> From: Kwok Cheung Yeung <kcy@codesourcery.com>
> Date: Mon, 6 Dec 2021 22:58:01 +0000
> Subject: [PATCH 5/7] openmp: Add C++ support for parsing metadirectives
>
> This adds support for parsing OpenMP metadirectives in the C++ front end.
>
> 2021-12-10 Kwok Cheung Yeung <kcy@codesourcery.com>
>
> gcc/cp/
> * parser.c (cp_parser_skip_to_end_of_statement): Handle parentheses.
> (cp_parser_skip_to_end_of_block_or_statement): Likewise.
> (cp_parser_omp_context_selector): Add extra argument. Allow
> non-constant expressions.
> (cp_parser_omp_context_selector_specification): Add extra argument and
> propagate to cp_parser_omp_context_selector.
> (analyze_metadirective_body): New.
> (cp_parser_omp_metadirective): New.
> (cp_parser_omp_construct): Handle PRAGMA_OMP_METADIRECTIVE.
> (cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE.
> ---
> gcc/cp/parser.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 417 insertions(+), 8 deletions(-)
>
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 6f273bfe21f..afbfe148949 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -3907,6 +3907,17 @@ cp_parser_skip_to_end_of_statement (cp_parser* parser)
> ++nesting_depth;
> break;
>
> + case CPP_OPEN_PAREN:
> + /* Track parentheses in case the statement is a standalone 'for'
> + statement - we want to skip over the semicolons separating the
> + operands. */
> + ++nesting_depth;
> + break;
> +
> + case CPP_CLOSE_PAREN:
> + --nesting_depth;
> + break;
> +
> case CPP_KEYWORD:
> if (token->keyword != RID__EXPORT
> && token->keyword != RID__MODULE
> @@ -3996,6 +4007,17 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
> nesting_depth++;
> break;
>
> + case CPP_OPEN_PAREN:
> + /* Track parentheses in case the statement is a standalone 'for'
> + statement - we want to skip over the semicolons separating the
> + operands. */
> + nesting_depth++;
> + break;
> +
> + case CPP_CLOSE_PAREN:
> + nesting_depth--;
> + break;
> +
Like for C FE, I think this is too risky.
> case CTX_PROPERTY_EXPR:
> - t = cp_parser_constant_expression (parser);
> + /* Allow non-constant expressions in metadirectives. */
> + t = metadirective_p
> + ? cp_parser_expression (parser)
> + : cp_parser_constant_expression (parser);
> if (t != error_mark_node)
> {
> t = fold_non_dependent_expr (t);
> - if (!value_dependent_expression_p (t)
> - && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
> - || !tree_fits_shwi_p (t)))
> + if (metadirective_p && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
Like in the other patch, but more importantly, if t is
type_dependent_expression_p, you shouldn't diagnose it, it might be
integral after instantiation. But it needs to be diagnosed later during
instantiation if it isn't integral then...
> + cp_token *token = cp_lexer_peek_token (parser->lexer);
> + bool stop = false;
> +
> + if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CASE))
> + in_case = true;
> + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
> + in_label_decl = true;
> +
> + switch (token->type)
> + {
> + case CPP_EOF:
> + break;
> + case CPP_NAME:
> + if ((!in_case
> + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
> + || in_label_decl)
> + labels.safe_push (token->u.value);
Similar thing as for C, identifier : can appear in various spots even when
it isn't a label, in C++ even in many more cases. Say
nested struct definition, struct S : T {}; or (that is for both C and C++)
e.g. bitfields struct V { int v : 13; };
> + goto add;
> + case CPP_OPEN_BRACE:
> + ++nesting_depth;
> + goto add;
> + case CPP_CLOSE_BRACE:
> + if (--nesting_depth == 0)
> + stop = true;
> + goto add;
> + case CPP_OPEN_PAREN:
> + ++bracket_depth;
> + goto add;
> + case CPP_CLOSE_PAREN:
> + --bracket_depth;
> + goto add;
> + case CPP_COLON:
> + in_case = false;
> + goto add;
> + case CPP_SEMICOLON:
> + if (nesting_depth == 0 && bracket_depth == 0)
> + stop = true;
> + /* Local label declarations are terminated by a semicolon. */
> + in_label_decl = false;
> + goto add;
> + default:
> + add:
> + tokens.safe_push (*token);
> + cp_lexer_consume_token (parser->lexer);
> + if (stop)
> + break;
> + continue;
> + }
> + break;
> + }
> +}
> +
> +/* OpenMP 5.0:
> +
> + # pragma omp metadirective [clause[, clause]]
> +*/
> +
> +static tree
> +cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
> + char *p_name, omp_clause_mask, tree *,
> + bool *if_p)
> +{
> + tree ret;
> + auto_vec<cp_token> directive_tokens;
> + auto_vec<cp_token> body_tokens;
> + auto_vec<tree> body_labels;
> + auto_vec<const struct c_omp_directive *> directives;
> + auto_vec<tree> ctxs;
> + bool default_seen = false;
> + int directive_token_idx = 0;
> + location_t loc = cp_lexer_peek_token (parser->lexer)->location;
> + tree standalone_body = NULL_TREE;
> + vec<struct omp_metadirective_variant> candidates;
> +
> + ret = make_node (OMP_METADIRECTIVE);
Better write tree ret = make_node ...
i.e. at least where easily possible declare vars on first use rather than
at the start of function.
Also, same comments I wrote in the C FE patch.
> + SET_EXPR_LOCATION (ret, loc);
> + TREE_TYPE (ret) = void_type_node;
> + OMP_METADIRECTIVE_CLAUSES (ret) = NULL_TREE;
> + strcat (p_name, " metadirective");
> +
> + while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
> + {
> + if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)
> + && cp_lexer_next_token_is_not (parser->lexer, CPP_KEYWORD))
> + {
> + cp_parser_error (parser, "expected %<when%> or %<default%>");
> + goto fail;
> + }
> +
> + location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
> + const char *p
> + = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value);
> + cp_lexer_consume_token (parser->lexer);
> + bool default_p = strcmp (p, "default") == 0;
> + if (default_p)
> + {
> + if (default_seen)
> + {
> + cp_parser_error (parser, "there can only be one default clause "
> + "in a metadirective");
> + goto fail;
> + }
> + else
> + default_seen = true;
> + }
> + if (!strcmp (p, "when") == 0 && !default_p)
> + {
> + cp_parser_error (parser, "expected %<when%> or %<default%>");
> + goto fail;
> + }
> +
> + matching_parens parens;
> + tree ctx = NULL_TREE;
> + bool skip = false;
> +
> + if (!parens.require_open (parser))
> + goto fail;
> +
> + if (!default_p)
> + {
> + ctx = cp_parser_omp_context_selector_specification (parser, false,
> + true);
> + if (ctx == error_mark_node)
> + goto fail;
> + ctx = omp_check_context_selector (match_loc, ctx);
> + if (ctx == error_mark_node)
> + goto fail;
> +
> + /* Remove the selector from further consideration if can be
> + evaluated as a non-match at this point. */
> + skip = (omp_context_selector_matches (ctx, true) == 0);
> +
> + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
> + {
> + cp_parser_error (parser, "expected colon");
> + goto fail;
> + }
> + cp_lexer_consume_token (parser->lexer);
> + }
> +
> + /* Read in the directive type and create a dummy pragma token for
> + it. */
> + location_t loc = cp_lexer_peek_token (parser->lexer)->location;
> +
> + p = NULL;
> + if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
> + p = "nothing";
> + else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
> + {
> + p = "for";
> + cp_lexer_consume_token (parser->lexer);
> + }
> + else if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
> + {
> + cp_token *token = cp_lexer_consume_token (parser->lexer);
> + p = IDENTIFIER_POINTER (token->u.value);
> + }
> +
> + if (p == NULL)
> + {
> + cp_parser_error (parser, "expected directive name");
> + goto fail;
> + }
> +
> + const struct c_omp_directive *omp_directive
> + = c_omp_categorize_directive (p, NULL, NULL);
> +
> + if (omp_directive == NULL)
> + {
> + cp_parser_error (parser, "unknown directive name");
> + goto fail;
> + }
> + if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE)
> + {
> + cp_parser_error (parser,
> + "metadirectives cannot be used as directive "
> + "variants");
> + goto fail;
> + }
> + if (omp_directive->kind == C_OMP_DIR_DECLARATIVE)
> + {
> + sorry_at (loc, "declarative directive variants are not supported");
> + goto fail;
> + }
> +
> + if (!skip)
> + {
> + cp_token pragma_token;
> + pragma_token.type = CPP_PRAGMA;
> + pragma_token.location = loc;
> + pragma_token.u.value = build_int_cst (NULL, omp_directive->id);
> +
> + directives.safe_push (omp_directive);
> + directive_tokens.safe_push (pragma_token);
> + ctxs.safe_push (ctx);
> + }
> +
> + /* Read in tokens for the directive clauses. */
> + int nesting_depth = 0;
> + while (1)
> + {
> + cp_token *token = cp_lexer_peek_token (parser->lexer);
> + switch (token->type)
> + {
> + case CPP_EOF:
> + case CPP_PRAGMA_EOL:
> + break;
> + case CPP_OPEN_PAREN:
> + ++nesting_depth;
> + goto add;
> + case CPP_CLOSE_PAREN:
> + if (nesting_depth-- == 0)
> + break;
> + goto add;
> + default:
> + add:
> + if (!skip)
> + directive_tokens.safe_push (*token);
> + cp_lexer_consume_token (parser->lexer);
> + continue;
> + }
> + break;
> + }
> +
> + cp_lexer_consume_token (parser->lexer);
> +
> + if (!skip)
> + {
> + cp_token eol_token = {};
> + eol_token.type = CPP_PRAGMA_EOL;
> + eol_token.keyword = RID_MAX;
> + directive_tokens.safe_push (eol_token);
> + }
> + }
> + cp_parser_skip_to_pragma_eol (parser, pragma_tok);
> +
> + if (!default_seen)
> + {
> + /* Add a default clause that evaluates to 'omp nothing'. */
> + const struct c_omp_directive *omp_directive
> + = c_omp_categorize_directive ("nothing", NULL, NULL);
> +
> + cp_token pragma_token = {};
> + pragma_token.type = CPP_PRAGMA;
> + pragma_token.keyword = RID_MAX;
> + pragma_token.location = UNKNOWN_LOCATION;
> + pragma_token.u.value = build_int_cst (NULL, PRAGMA_OMP_NOTHING);
> +
> + directives.safe_push (omp_directive);
> + directive_tokens.safe_push (pragma_token);
> + ctxs.safe_push (NULL_TREE);
> +
> + cp_token eol_token = {};
> + eol_token.type = CPP_PRAGMA_EOL;
> + eol_token.keyword = RID_MAX;
> + directive_tokens.safe_push (eol_token);
> + }
> +
> + analyze_metadirective_body (parser, body_tokens, body_labels);
> +
> + /* Process each candidate directive. */
> + unsigned i;
> + tree ctx;
> + cp_lexer *lexer;
> +
> + lexer = cp_lexer_alloc ();
> + lexer->debugging_p = parser->lexer->debugging_p;
> + vec_safe_reserve (lexer->buffer,
> + directive_tokens.length () + body_tokens.length () + 2);
> +
> + FOR_EACH_VEC_ELT (ctxs, i, ctx)
> + {
> + lexer->buffer->truncate (0);
> +
> + /* Add the directive tokens. */
> + do
> + lexer->buffer->quick_push (directive_tokens [directive_token_idx++]);
> + while (lexer->buffer->last ().type != CPP_PRAGMA_EOL);
> +
> + /* Add the body tokens. */
> + for (unsigned j = 0; j < body_tokens.length (); j++)
> + lexer->buffer->quick_push (body_tokens[j]);
> +
> + /* Make sure nothing tries to read past the end of the tokens. */
> + cp_token eof_token = {};
> + eof_token.type = CPP_EOF;
> + eof_token.keyword = RID_MAX;
> + lexer->buffer->quick_push (eof_token);
> + lexer->buffer->quick_push (eof_token);
> +
> + lexer->next_token = lexer->buffer->address();
> + lexer->last_token = lexer->next_token + lexer->buffer->length () - 1;
> +
> + cp_lexer *old_lexer = parser->lexer;
> + parser->lexer = lexer;
> + cp_lexer_set_source_position_from_token (lexer->next_token);
> +
> + tree directive = push_stmt_list ();
> + tree directive_stmt = begin_compound_stmt (0);
> +
> + /* Declare all labels that occur within the directive body as
> + local. */
> + for (unsigned j = 0; j < body_labels.length (); j++)
> + finish_label_decl (body_labels[j]);
> + cp_parser_pragma (parser, pragma_compound, if_p);
> +
> + finish_compound_stmt (directive_stmt);
> + directive = pop_stmt_list (directive);
> +
> + bool standalone_p
> + = directives[i]->kind == C_OMP_DIR_STANDALONE
> + || directives[i]->kind == C_OMP_DIR_UTILITY;
> + if (standalone_p)
> + {
> + /* Parsing standalone directives will not consume the body
> + tokens, so do that here. */
> + if (standalone_body == NULL_TREE)
> + {
> + standalone_body = push_stmt_list ();
> + cp_parser_statement (parser, NULL_TREE, false, if_p);
> + standalone_body = pop_stmt_list (standalone_body);
> + }
> + else
> + cp_parser_skip_to_end_of_block_or_statement (parser);
> + }
> +
> + tree body = standalone_p ? standalone_body : NULL_TREE;
> + tree variant = build_tree_list (ctx, build_tree_list (directive, body));
> + OMP_METADIRECTIVE_CLAUSES (ret)
> + = chainon (OMP_METADIRECTIVE_CLAUSES (ret), variant);
> +
> + /* Check that all valid tokens have been consumed. */
> + gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EOF));
> +
> + parser->lexer = old_lexer;
> + cp_lexer_set_source_position_from_token (old_lexer->next_token);
> + }
> +
> + /* Try to resolve the metadirective early. */
> + candidates = omp_resolve_metadirective (ret);
> + if (!candidates.is_empty ())
> + ret = c_omp_expand_metadirective (candidates);
> +
> + add_stmt (ret);
> +
> + return ret;
> +
> +fail:
> + /* Skip the metadirective pragma. */
> + cp_parser_skip_to_pragma_eol (parser, pragma_tok);
> +
> + /* Skip the metadirective body. */
> + cp_parser_skip_to_end_of_block_or_statement (parser);
> + return error_mark_node;
> +}
> +
> +
> /* Helper function of cp_parser_omp_declare_reduction. Parse the combiner
> expression and optional initializer clause of
> #pragma omp declare reduction. We store the expression(s) as
> @@ -47077,6 +47480,11 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
> stmt = cp_parser_omp_master (parser, pragma_tok, p_name, mask, NULL,
> if_p);
> break;
> + case PRAGMA_OMP_METADIRECTIVE:
> + strcpy (p_name, "#pragma omp");
> + stmt = cp_parser_omp_metadirective (parser, pragma_tok, p_name, mask,
> + NULL, if_p);
> + break;
> case PRAGMA_OMP_PARALLEL:
> strcpy (p_name, "#pragma omp");
> stmt = cp_parser_omp_parallel (parser, pragma_tok, p_name, mask, NULL,
> @@ -47727,6 +48135,7 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
> case PRAGMA_OMP_LOOP:
> case PRAGMA_OMP_MASKED:
> case PRAGMA_OMP_MASTER:
> + case PRAGMA_OMP_METADIRECTIVE:
> case PRAGMA_OMP_PARALLEL:
> case PRAGMA_OMP_SCOPE:
> case PRAGMA_OMP_SECTIONS:
> --
I miss handling of OMP_METADIRECTIVE in pt.c and testsuite coverage
of metadirectives in templates (both function templates and class templates
in whose methods metadirectives are used).
And something I forgot to note in the C FE patch, there is the
"The context selector that appears in a when clause must not specify any properties
for the simd selector."
restriction I haven't seen being checked for (and tested in the testsuite).
Jakub
@@ -3907,6 +3907,17 @@ cp_parser_skip_to_end_of_statement (cp_parser* parser)
++nesting_depth;
break;
+ case CPP_OPEN_PAREN:
+ /* Track parentheses in case the statement is a standalone 'for'
+ statement - we want to skip over the semicolons separating the
+ operands. */
+ ++nesting_depth;
+ break;
+
+ case CPP_CLOSE_PAREN:
+ --nesting_depth;
+ break;
+
case CPP_KEYWORD:
if (token->keyword != RID__EXPORT
&& token->keyword != RID__MODULE
@@ -3996,6 +4007,17 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
nesting_depth++;
break;
+ case CPP_OPEN_PAREN:
+ /* Track parentheses in case the statement is a standalone 'for'
+ statement - we want to skip over the semicolons separating the
+ operands. */
+ nesting_depth++;
+ break;
+
+ case CPP_CLOSE_PAREN:
+ nesting_depth--;
+ break;
+
case CPP_KEYWORD:
if (token->keyword != RID__EXPORT
&& token->keyword != RID__MODULE
@@ -44972,7 +44994,8 @@ static const char *const omp_user_selectors[] = {
score(score-expression) */
static tree
-cp_parser_omp_context_selector (cp_parser *parser, tree set, bool has_parms_p)
+cp_parser_omp_context_selector (cp_parser *parser, tree set, bool has_parms_p,
+ bool metadirective_p)
{
tree ret = NULL_TREE;
do
@@ -45188,15 +45211,21 @@ cp_parser_omp_context_selector (cp_parser *parser, tree set, bool has_parms_p)
while (1);
break;
case CTX_PROPERTY_EXPR:
- t = cp_parser_constant_expression (parser);
+ /* Allow non-constant expressions in metadirectives. */
+ t = metadirective_p
+ ? cp_parser_expression (parser)
+ : cp_parser_constant_expression (parser);
if (t != error_mark_node)
{
t = fold_non_dependent_expr (t);
- if (!value_dependent_expression_p (t)
- && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
- || !tree_fits_shwi_p (t)))
+ if (metadirective_p && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
error_at (token->location, "property must be "
- "constant integer expression");
+ "integer expression");
+ else if (!metadirective_p && !value_dependent_expression_p (t)
+ && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+ || !tree_fits_shwi_p (t)))
+ error_at (token->location, "property must be constant "
+ "integer expression");
else
properties = tree_cons (NULL_TREE, t, properties);
}
@@ -45260,7 +45289,8 @@ cp_parser_omp_context_selector (cp_parser *parser, tree set, bool has_parms_p)
static tree
cp_parser_omp_context_selector_specification (cp_parser *parser,
- bool has_parms_p)
+ bool has_parms_p,
+ bool metadirective_p = false)
{
tree ret = NULL_TREE;
do
@@ -45308,7 +45338,8 @@ cp_parser_omp_context_selector_specification (cp_parser *parser,
return error_mark_node;
tree selectors
- = cp_parser_omp_context_selector (parser, set, has_parms_p);
+ = cp_parser_omp_context_selector (parser, set, has_parms_p,
+ metadirective_p);
if (selectors == error_mark_node)
{
cp_parser_skip_to_closing_brace (parser);
@@ -45831,6 +45862,378 @@ cp_parser_omp_end_declare_target (cp_parser *parser, cp_token *pragma_tok)
}
}
+
+/* Helper function for c_parser_omp_metadirective. */
+
+static void
+analyze_metadirective_body (cp_parser *parser,
+ vec<cp_token> &tokens,
+ vec<tree> &labels)
+{
+ int nesting_depth = 0;
+ int bracket_depth = 0;
+ bool in_case = false;
+ bool in_label_decl = false;
+
+ while (1)
+ {
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ bool stop = false;
+
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CASE))
+ in_case = true;
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
+ in_label_decl = true;
+
+ switch (token->type)
+ {
+ case CPP_EOF:
+ break;
+ case CPP_NAME:
+ if ((!in_case
+ && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON))
+ || in_label_decl)
+ labels.safe_push (token->u.value);
+ goto add;
+ case CPP_OPEN_BRACE:
+ ++nesting_depth;
+ goto add;
+ case CPP_CLOSE_BRACE:
+ if (--nesting_depth == 0)
+ stop = true;
+ goto add;
+ case CPP_OPEN_PAREN:
+ ++bracket_depth;
+ goto add;
+ case CPP_CLOSE_PAREN:
+ --bracket_depth;
+ goto add;
+ case CPP_COLON:
+ in_case = false;
+ goto add;
+ case CPP_SEMICOLON:
+ if (nesting_depth == 0 && bracket_depth == 0)
+ stop = true;
+ /* Local label declarations are terminated by a semicolon. */
+ in_label_decl = false;
+ goto add;
+ default:
+ add:
+ tokens.safe_push (*token);
+ cp_lexer_consume_token (parser->lexer);
+ if (stop)
+ break;
+ continue;
+ }
+ break;
+ }
+}
+
+/* OpenMP 5.0:
+
+ # pragma omp metadirective [clause[, clause]]
+*/
+
+static tree
+cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
+ char *p_name, omp_clause_mask, tree *,
+ bool *if_p)
+{
+ tree ret;
+ auto_vec<cp_token> directive_tokens;
+ auto_vec<cp_token> body_tokens;
+ auto_vec<tree> body_labels;
+ auto_vec<const struct c_omp_directive *> directives;
+ auto_vec<tree> ctxs;
+ bool default_seen = false;
+ int directive_token_idx = 0;
+ location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+ tree standalone_body = NULL_TREE;
+ vec<struct omp_metadirective_variant> candidates;
+
+ ret = make_node (OMP_METADIRECTIVE);
+ SET_EXPR_LOCATION (ret, loc);
+ TREE_TYPE (ret) = void_type_node;
+ OMP_METADIRECTIVE_CLAUSES (ret) = NULL_TREE;
+ strcat (p_name, " metadirective");
+
+ while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+ {
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)
+ && cp_lexer_next_token_is_not (parser->lexer, CPP_KEYWORD))
+ {
+ cp_parser_error (parser, "expected %<when%> or %<default%>");
+ goto fail;
+ }
+
+ location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
+ const char *p
+ = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value);
+ cp_lexer_consume_token (parser->lexer);
+ bool default_p = strcmp (p, "default") == 0;
+ if (default_p)
+ {
+ if (default_seen)
+ {
+ cp_parser_error (parser, "there can only be one default clause "
+ "in a metadirective");
+ goto fail;
+ }
+ else
+ default_seen = true;
+ }
+ if (!strcmp (p, "when") == 0 && !default_p)
+ {
+ cp_parser_error (parser, "expected %<when%> or %<default%>");
+ goto fail;
+ }
+
+ matching_parens parens;
+ tree ctx = NULL_TREE;
+ bool skip = false;
+
+ if (!parens.require_open (parser))
+ goto fail;
+
+ if (!default_p)
+ {
+ ctx = cp_parser_omp_context_selector_specification (parser, false,
+ true);
+ if (ctx == error_mark_node)
+ goto fail;
+ ctx = omp_check_context_selector (match_loc, ctx);
+ if (ctx == error_mark_node)
+ goto fail;
+
+ /* Remove the selector from further consideration if can be
+ evaluated as a non-match at this point. */
+ skip = (omp_context_selector_matches (ctx, true) == 0);
+
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+ {
+ cp_parser_error (parser, "expected colon");
+ goto fail;
+ }
+ cp_lexer_consume_token (parser->lexer);
+ }
+
+ /* Read in the directive type and create a dummy pragma token for
+ it. */
+ location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+
+ p = NULL;
+ if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+ p = "nothing";
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
+ {
+ p = "for";
+ cp_lexer_consume_token (parser->lexer);
+ }
+ else if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ {
+ cp_token *token = cp_lexer_consume_token (parser->lexer);
+ p = IDENTIFIER_POINTER (token->u.value);
+ }
+
+ if (p == NULL)
+ {
+ cp_parser_error (parser, "expected directive name");
+ goto fail;
+ }
+
+ const struct c_omp_directive *omp_directive
+ = c_omp_categorize_directive (p, NULL, NULL);
+
+ if (omp_directive == NULL)
+ {
+ cp_parser_error (parser, "unknown directive name");
+ goto fail;
+ }
+ if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE)
+ {
+ cp_parser_error (parser,
+ "metadirectives cannot be used as directive "
+ "variants");
+ goto fail;
+ }
+ if (omp_directive->kind == C_OMP_DIR_DECLARATIVE)
+ {
+ sorry_at (loc, "declarative directive variants are not supported");
+ goto fail;
+ }
+
+ if (!skip)
+ {
+ cp_token pragma_token;
+ pragma_token.type = CPP_PRAGMA;
+ pragma_token.location = loc;
+ pragma_token.u.value = build_int_cst (NULL, omp_directive->id);
+
+ directives.safe_push (omp_directive);
+ directive_tokens.safe_push (pragma_token);
+ ctxs.safe_push (ctx);
+ }
+
+ /* Read in tokens for the directive clauses. */
+ int nesting_depth = 0;
+ while (1)
+ {
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ switch (token->type)
+ {
+ case CPP_EOF:
+ case CPP_PRAGMA_EOL:
+ break;
+ case CPP_OPEN_PAREN:
+ ++nesting_depth;
+ goto add;
+ case CPP_CLOSE_PAREN:
+ if (nesting_depth-- == 0)
+ break;
+ goto add;
+ default:
+ add:
+ if (!skip)
+ directive_tokens.safe_push (*token);
+ cp_lexer_consume_token (parser->lexer);
+ continue;
+ }
+ break;
+ }
+
+ cp_lexer_consume_token (parser->lexer);
+
+ if (!skip)
+ {
+ cp_token eol_token = {};
+ eol_token.type = CPP_PRAGMA_EOL;
+ eol_token.keyword = RID_MAX;
+ directive_tokens.safe_push (eol_token);
+ }
+ }
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+
+ if (!default_seen)
+ {
+ /* Add a default clause that evaluates to 'omp nothing'. */
+ const struct c_omp_directive *omp_directive
+ = c_omp_categorize_directive ("nothing", NULL, NULL);
+
+ cp_token pragma_token = {};
+ pragma_token.type = CPP_PRAGMA;
+ pragma_token.keyword = RID_MAX;
+ pragma_token.location = UNKNOWN_LOCATION;
+ pragma_token.u.value = build_int_cst (NULL, PRAGMA_OMP_NOTHING);
+
+ directives.safe_push (omp_directive);
+ directive_tokens.safe_push (pragma_token);
+ ctxs.safe_push (NULL_TREE);
+
+ cp_token eol_token = {};
+ eol_token.type = CPP_PRAGMA_EOL;
+ eol_token.keyword = RID_MAX;
+ directive_tokens.safe_push (eol_token);
+ }
+
+ analyze_metadirective_body (parser, body_tokens, body_labels);
+
+ /* Process each candidate directive. */
+ unsigned i;
+ tree ctx;
+ cp_lexer *lexer;
+
+ lexer = cp_lexer_alloc ();
+ lexer->debugging_p = parser->lexer->debugging_p;
+ vec_safe_reserve (lexer->buffer,
+ directive_tokens.length () + body_tokens.length () + 2);
+
+ FOR_EACH_VEC_ELT (ctxs, i, ctx)
+ {
+ lexer->buffer->truncate (0);
+
+ /* Add the directive tokens. */
+ do
+ lexer->buffer->quick_push (directive_tokens [directive_token_idx++]);
+ while (lexer->buffer->last ().type != CPP_PRAGMA_EOL);
+
+ /* Add the body tokens. */
+ for (unsigned j = 0; j < body_tokens.length (); j++)
+ lexer->buffer->quick_push (body_tokens[j]);
+
+ /* Make sure nothing tries to read past the end of the tokens. */
+ cp_token eof_token = {};
+ eof_token.type = CPP_EOF;
+ eof_token.keyword = RID_MAX;
+ lexer->buffer->quick_push (eof_token);
+ lexer->buffer->quick_push (eof_token);
+
+ lexer->next_token = lexer->buffer->address();
+ lexer->last_token = lexer->next_token + lexer->buffer->length () - 1;
+
+ cp_lexer *old_lexer = parser->lexer;
+ parser->lexer = lexer;
+ cp_lexer_set_source_position_from_token (lexer->next_token);
+
+ tree directive = push_stmt_list ();
+ tree directive_stmt = begin_compound_stmt (0);
+
+ /* Declare all labels that occur within the directive body as
+ local. */
+ for (unsigned j = 0; j < body_labels.length (); j++)
+ finish_label_decl (body_labels[j]);
+ cp_parser_pragma (parser, pragma_compound, if_p);
+
+ finish_compound_stmt (directive_stmt);
+ directive = pop_stmt_list (directive);
+
+ bool standalone_p
+ = directives[i]->kind == C_OMP_DIR_STANDALONE
+ || directives[i]->kind == C_OMP_DIR_UTILITY;
+ if (standalone_p)
+ {
+ /* Parsing standalone directives will not consume the body
+ tokens, so do that here. */
+ if (standalone_body == NULL_TREE)
+ {
+ standalone_body = push_stmt_list ();
+ cp_parser_statement (parser, NULL_TREE, false, if_p);
+ standalone_body = pop_stmt_list (standalone_body);
+ }
+ else
+ cp_parser_skip_to_end_of_block_or_statement (parser);
+ }
+
+ tree body = standalone_p ? standalone_body : NULL_TREE;
+ tree variant = build_tree_list (ctx, build_tree_list (directive, body));
+ OMP_METADIRECTIVE_CLAUSES (ret)
+ = chainon (OMP_METADIRECTIVE_CLAUSES (ret), variant);
+
+ /* Check that all valid tokens have been consumed. */
+ gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EOF));
+
+ parser->lexer = old_lexer;
+ cp_lexer_set_source_position_from_token (old_lexer->next_token);
+ }
+
+ /* Try to resolve the metadirective early. */
+ candidates = omp_resolve_metadirective (ret);
+ if (!candidates.is_empty ())
+ ret = c_omp_expand_metadirective (candidates);
+
+ add_stmt (ret);
+
+ return ret;
+
+fail:
+ /* Skip the metadirective pragma. */
+ cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+
+ /* Skip the metadirective body. */
+ cp_parser_skip_to_end_of_block_or_statement (parser);
+ return error_mark_node;
+}
+
+
/* Helper function of cp_parser_omp_declare_reduction. Parse the combiner
expression and optional initializer clause of
#pragma omp declare reduction. We store the expression(s) as
@@ -47077,6 +47480,11 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
stmt = cp_parser_omp_master (parser, pragma_tok, p_name, mask, NULL,
if_p);
break;
+ case PRAGMA_OMP_METADIRECTIVE:
+ strcpy (p_name, "#pragma omp");
+ stmt = cp_parser_omp_metadirective (parser, pragma_tok, p_name, mask,
+ NULL, if_p);
+ break;
case PRAGMA_OMP_PARALLEL:
strcpy (p_name, "#pragma omp");
stmt = cp_parser_omp_parallel (parser, pragma_tok, p_name, mask, NULL,
@@ -47727,6 +48135,7 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
case PRAGMA_OMP_LOOP:
case PRAGMA_OMP_MASKED:
case PRAGMA_OMP_MASTER:
+ case PRAGMA_OMP_METADIRECTIVE:
case PRAGMA_OMP_PARALLEL:
case PRAGMA_OMP_SCOPE:
case PRAGMA_OMP_SECTIONS: