[5/7] openmp: Add C++ support for parsing metadirectives

Message ID 8e830d64-4a71-2799-fda4-5ca77917f832@codesourcery.com
State New
Headers
Series openmp: OpenMP metadirectives support |

Commit Message

Kwok Cheung Yeung Dec. 10, 2021, 5:37 p.m. UTC
  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

Jakub Jelinek May 30, 2022, 11:52 a.m. UTC | #1
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
  

Patch

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;
+
 	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: