Additional shared C/C++ testcases are included in a subsequent patch in this
series.

gcc/cp/ChangeLog
        PR middle-end/112779
        PR middle-end/113904
        * cp-tree.h (struct saved_scope): Add new field
        x_processing_omp_trait_property_expr.
        (processing_omp_trait_property_expr): New.
        (cp_parser_skip_to_end_of_block_or_statement): Add metadirective_p
        parameter and handle skipping over the parentheses in a "for"
        statement.
        (struct omp_metadirective_parse_data): New.
        (mangle_metadirective_region_label): New.
        (cp_parser_label_for_labeled_statement): Mangle label names in a
        metadirective body.
        (cp_parser_jump_statement): Likewise.
        (cp_parser_omp_context_selector): Allow arbitrary expressions in
        device_num and condition properties.
        (analyze_metadirective_body): New.
        (cp_parser_omp_metadirective): New.
        (cp_parser_pragma): Handle PRAGMA_OMP_METADIRECTIVE.
        * parser.h (struct cp_parser): Add omp_metadirective_state field.
        * pt.cc (tsubst_omp_context_selector): New.
        (tsubst_stmt): Handle OMP_METADIRECTIVE.
        * semantics.cc (finish_id_expression_1): Don't diagnose use of
        parameter outside function body in dynamic selector expressions here.

        gcc/testsuite/
        PR middle-end/112779
        PR middle-end/113904
        * c-c++-common/gomp/declare-variant-2.c: Adjust output for C++.
        * g++.dg/gomp/declare-variant-class-1.C: New.
        * g++.dg/gomp/declare-variant-class-2.C: New.
        * g++.dg/gomp/metadirective-template-1.C: New.

        libgomp/
        PR middle-end/112779
        PR middle-end/113904
        * testsuite/libgomp.c++/metadirective-template-1.C: New.
        * testsuite/libgomp.c++/metadirective-template-2.C: New.
        * testsuite/libgomp.c++/metadirective-template-3.C: New.

Co-Authored-By: Kwok Cheung Yeung <k...@codesourcery.com>
Co-Authored-By: Sandra Loosemore <san...@codesourcery.com>
---
 gcc/cp/cp-tree.h                              |   2 +
 gcc/cp/parser.cc                              | 540 +++++++++++++++++-
 gcc/cp/parser.h                               |   6 +
 gcc/cp/pt.cc                                  | 126 ++++
 gcc/cp/semantics.cc                           |   3 +-
 .../c-c++-common/gomp/declare-variant-2.c     |   4 +-
 .../g++.dg/gomp/declare-variant-class-1.C     |  32 ++
 .../g++.dg/gomp/declare-variant-class-2.C     |  37 ++
 .../g++.dg/gomp/metadirective-template-1.C    |  74 +++
 .../libgomp.c++/metadirective-template-1.C    |  39 ++
 .../libgomp.c++/metadirective-template-2.C    |  43 ++
 .../libgomp.c++/metadirective-template-3.C    |  43 ++
 12 files changed, 926 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/metadirective-template-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/metadirective-template-3.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5f0529530b9..7875c5d0a57 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1929,6 +1929,7 @@ struct GTY(()) saved_scope {
   int suppress_location_wrappers;
   BOOL_BITFIELD x_processing_explicit_instantiation : 1;
   BOOL_BITFIELD need_pop_function_context : 1;
+  BOOL_BITFIELD x_processing_omp_trait_property_expr : 1;
 
   /* Nonzero if we are parsing the discarded statement of a constexpr
      if-statement.  */
@@ -2000,6 +2001,7 @@ extern GTY(()) struct saved_scope *scope_chain;
 #define processing_template_decl scope_chain->x_processing_template_decl
 #define processing_specialization scope_chain->x_processing_specialization
 #define processing_explicit_instantiation 
scope_chain->x_processing_explicit_instantiation
+#define processing_omp_trait_property_expr 
scope_chain->x_processing_omp_trait_property_expr
 
 /* Nonzero if we are parsing the conditional expression of a contract
    condition. These expressions appear outside the paramter list (like a
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 666e034dd5b..c9b0401de60 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -3020,7 +3020,7 @@ static void cp_parser_skip_to_end_of_statement
 static void cp_parser_consume_semicolon_at_end_of_statement
   (cp_parser *);
 static void cp_parser_skip_to_end_of_block_or_statement
-  (cp_parser *);
+  (cp_parser *, bool = false);
 static bool cp_parser_skip_to_closing_brace
   (cp_parser *);
 static bool cp_parser_skip_entire_template_parameter_list
@@ -4245,9 +4245,11 @@ cp_parser_consume_semicolon_at_end_of_statement 
(cp_parser *parser)
    have consumed a non-nested `;'.  */
 
 static void
-cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
+cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser,
+                                            bool metadirective_p)
 {
   int nesting_depth = 0;
+  int bracket_depth = 0;
 
   /* Unwind generic function template scope if necessary.  */
   if (parser->fully_implicit_function_template_p)
@@ -4269,7 +4271,7 @@ cp_parser_skip_to_end_of_block_or_statement (cp_parser* 
parser)
 
        case CPP_SEMICOLON:
          /* Stop if this is an unnested ';'. */
-         if (!nesting_depth)
+         if (!nesting_depth && (!metadirective_p || bracket_depth <= 0))
            nesting_depth = -1;
          break;
 
@@ -4288,6 +4290,19 @@ 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.  */
+         if (metadirective_p && nesting_depth == 0)
+           bracket_depth++;
+         break;
+
+       case CPP_CLOSE_PAREN:
+         if (metadirective_p && nesting_depth == 0)
+           bracket_depth--;
+         break;
+
        case CPP_KEYWORD:
          if (!cp_token_is_module_directive (token))
            break;
@@ -13269,6 +13284,33 @@ attr_chainon (tree attrs, tree attr)
   return chainon (attrs, attr);
 }
 
+
+/* State object for mangling labels in a metadirective region.  */
+
+/* Information used while parsing an OpenMP metadirective.  */
+struct omp_metadirective_parse_data {
+  /* These fields are used to unique-ify labels when reparsing the
+     code in a metadirective alternative.  */
+  vec<tree> * GTY((skip)) body_labels;
+  unsigned int region_num;
+};
+
+/* Helper function for cp_parser_label: mangle a metadirective region
+   label NAME if necessary.  */
+static tree
+mangle_metadirective_region_label (cp_parser *parser, tree name)
+{
+  if (parser->omp_metadirective_state->body_labels->contains (name))
+    {
+      const char *old_name = IDENTIFIER_POINTER (name);
+      char *new_name = (char *) alloca (strlen (old_name) + 32);
+      sprintf (new_name, "%s_MDR%u", old_name,
+              parser->omp_metadirective_state->region_num);
+      return get_identifier (new_name);
+    }
+  return name;
+}
+
 /* Parse the label for a labeled-statement, i.e.
 
    label:
@@ -13371,7 +13413,11 @@ cp_parser_label_for_labeled_statement (cp_parser* 
parser, tree attributes)
 
     default:
       /* Anything else must be an ordinary label.  */
-      label = finish_label_stmt (cp_parser_identifier (parser));
+      cp_expr identifier = cp_parser_identifier (parser);
+      if (identifier != error_mark_node
+         && parser->omp_metadirective_state)
+       *identifier = mangle_metadirective_region_label (parser, *identifier);
+      label = finish_label_stmt (identifier);
       if (label && TREE_CODE (label) == LABEL_DECL)
        {
          FALLTHROUGH_LABEL_P (label) = fallthrough_p;
@@ -15246,7 +15292,13 @@ cp_parser_jump_statement (cp_parser* parser, tree 
&std_attrs)
          finish_goto_stmt (cp_parser_expression (parser));
        }
       else
-       finish_goto_stmt (cp_parser_identifier (parser));
+       {
+         cp_expr identifier = cp_parser_identifier (parser);
+         if (identifier != error_mark_node && parser->omp_metadirective_state)
+           *identifier = mangle_metadirective_region_label (parser,
+                                                            *identifier);
+         finish_goto_stmt (identifier);
+       }
       /* Look for the final `;'.  */
       cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
       break;
@@ -49509,23 +49561,25 @@ cp_parser_omp_context_selector (cp_parser *parser, 
enum omp_tss_code set,
              break;
            case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR:
            case OMP_TRAIT_PROPERTY_BOOL_EXPR:
-             /* FIXME: this is bogus, the expression need
-                not be constant.  */
-             t = cp_parser_constant_expression (parser);
-             if (t != error_mark_node)
+             processing_omp_trait_property_expr = true;
+             /* This actually parses a not-necessarily-constant
+                conditional-expression.  */
+             t = cp_parser_constant_expression (parser, true, NULL, false);
+             processing_omp_trait_property_expr = false;
+             if (t == error_mark_node)
+               return error_mark_node;
+             if (!type_dependent_expression_p (t)
+                 && !value_dependent_expression_p (t))
                {
                  t = fold_non_dependent_expr (t);
-                 if (!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 = make_trait_property (NULL_TREE, t,
-                                                     properties);
+                 if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
+                   {
+                     error_at (token->location,
+                               "property must be integer expression");
+                     return error_mark_node;
+                   }
                }
-             else
-               return error_mark_node;
+             properties = make_trait_property (NULL_TREE, t, properties);
              break;
            case OMP_TRAIT_PROPERTY_CLAUSE_LIST:
              if (sel == OMP_TRAIT_CONSTRUCT_SIMD)
@@ -50872,6 +50926,450 @@ cp_parser_omp_end (cp_parser *parser, cp_token 
*pragma_tok)
     }
 }
 
+
+/* Helper function for cp_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;
+  cp_token *pragma_tok = NULL;
+
+  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 && bracket_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;
+       case CPP_PRAGMA:
+         parser->lexer->in_pragma = true;
+         pragma_tok = token;
+         goto add;
+       case CPP_PRAGMA_EOL:
+         /* C++ attribute syntax for OMP directives lexes as a pragma,
+            but we must reset the associated lexer state when we reach
+            the end in order to get the tokens for the statement that
+            come after it.  */
+         tokens.safe_push (*token);
+         cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+         pragma_tok = NULL;
+         continue;
+       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 void
+cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok,
+                            bool *if_p)
+{
+  static unsigned int metadirective_region_count = 0;
+
+  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 pragma_loc = pragma_tok->location;
+  tree standalone_body = NULL_TREE;
+  vec<struct omp_variant> candidates;
+  bool requires_body = false;
+
+  tree ret = make_node (OMP_METADIRECTIVE);
+  SET_EXPR_LOCATION (ret, pragma_loc);
+  TREE_TYPE (ret) = void_type_node;
+  OMP_METADIRECTIVE_VARIANTS (ret) = NULL_TREE;
+
+  while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+    {
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+       cp_lexer_consume_token (parser->lexer);
+      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%>, "
+                          "%<otherwise%>, or %<default%> clause");
+         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 || strcmp (p, "otherwise") == 0;
+      if (default_p)
+       {
+         if (default_seen)
+           {
+             error_at (match_loc, "too many %<otherwise%> or %<default%> "
+                       "clauses in %<metadirective%>");
+             cp_parser_skip_to_end_of_block_or_statement (parser, true);
+             goto fail;
+           }
+         else
+           default_seen = true;
+       }
+      else if (default_seen)
+       {
+         error_at (match_loc, "%<otherwise%> or %<default%> clause "
+                   "must appear last in %<metadirective%>");
+         cp_parser_skip_to_end_of_block_or_statement (parser, true);
+         goto fail;
+       }
+      if (!strcmp (p, "when") == 0 && !default_p)
+       {
+         error_at (match_loc, "%qs is not valid for %qs",
+                   p, "metadirective");
+         cp_parser_skip_to_end_of_block_or_statement (parser, true);
+         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);
+         if (ctx == error_mark_node)
+           goto fail;
+         ctx = omp_check_context_selector (match_loc, ctx, true);
+         if (ctx == error_mark_node)
+           goto fail;
+
+         /* Remove the selector from further consideration if it can be
+            evaluated as a non-match at this point.  */
+         /* FIXME: we could still do this if the context selector
+            doesn't have any dependent subexpressions.  */
+         skip = (!processing_template_decl
+                 && !omp_context_selector_matches (ctx, NULL_TREE, false));
+         if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+           {
+             cp_parser_require (parser, CPP_COLON, RT_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;
+
+      const char *directive[3] = {};
+      int i;
+      for (i = 0; i < 3; i++)
+       {
+         tree id;
+         if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type
+             == CPP_CLOSE_PAREN)
+           {
+             if (i == 0)
+               directive[i++] = "nothing";
+             break;
+           }
+         else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->type
+                  == CPP_NAME)
+           id = cp_lexer_peek_nth_token (parser->lexer, i + 1)->u.value;
+         else if (cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword
+                  != RID_MAX)
+           {
+             enum rid rid
+               = cp_lexer_peek_nth_token (parser->lexer, i + 1)->keyword;
+             id = ridpointers[rid];
+           }
+         else
+           break;
+
+         directive[i] = IDENTIFIER_POINTER (id);
+       }
+      if (i == 0)
+       {
+         error_at (loc, "expected directive name");
+         cp_parser_skip_to_end_of_block_or_statement (parser, true);
+         goto fail;
+       }
+
+      const struct c_omp_directive *omp_directive
+       = c_omp_categorize_directive (directive[0],
+                                     directive[1],
+                                     directive[2]);
+
+      if (omp_directive == NULL)
+       {
+         for (int j = 0; j < i; j++)
+           cp_lexer_consume_token (parser->lexer);
+         cp_parser_error (parser, "unknown directive name");
+         goto fail;
+       }
+      else
+       {
+         int token_count = 0;
+         if (omp_directive->first) token_count++;
+         if (omp_directive->second) token_count++;
+         if (omp_directive->third) token_count++;
+         for (int j = 0; j < token_count; j++)
+           cp_lexer_consume_token (parser->lexer);
+       }
+      if (p == NULL)
+       {
+         cp_parser_error (parser, "expected directive name");
+         goto fail;
+       }
+      if (omp_directive->id == PRAGMA_OMP_METADIRECTIVE)
+       {
+         cp_parser_error (parser,
+                          "metadirectives cannot be used as variants of a "
+                          "%<metadirective%>");
+         goto fail;
+       }
+      if (omp_directive->kind == C_OMP_DIR_DECLARATIVE)
+       {
+         sorry_at (loc, "declarative directive variants of a "
+                        "%<metadirective%> are not supported");
+         goto fail;
+       }
+      if (omp_directive->kind == C_OMP_DIR_CONSTRUCT)
+       requires_body = true;
+
+      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);
+    }
+
+  if (requires_body)
+    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.  */
+      gcc_assert (requires_body || body_tokens.is_empty ());
+      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;
+      struct omp_metadirective_parse_data *old_state
+       = parser->omp_metadirective_state;
+      parser->lexer = lexer;
+      cp_lexer_set_source_position_from_token (lexer->next_token);
+      struct omp_metadirective_parse_data new_state;
+      new_state.body_labels = &body_labels;
+      new_state.region_num = ++metadirective_region_count;
+      parser->omp_metadirective_state = &new_state;
+
+      int prev_errorcount = errorcount;
+      tree directive = push_stmt_list ();
+      tree directive_stmt = begin_compound_stmt (0);
+
+      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 && requires_body)
+       {
+         /* 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, true);
+       }
+
+      tree body = standalone_p ? standalone_body : NULL_TREE;
+      tree variant = make_omp_metadirective_variant (ctx, directive, body);
+      OMP_METADIRECTIVE_VARIANTS (ret)
+       = chainon (OMP_METADIRECTIVE_VARIANTS (ret), variant);
+
+      /* Check that all valid tokens have been consumed if no parse errors
+        encountered.  */
+      gcc_assert (errorcount != prev_errorcount
+                 || cp_lexer_next_token_is (parser->lexer, CPP_EOF));
+
+      parser->lexer = old_lexer;
+      cp_lexer_set_source_position_from_token (old_lexer->next_token);
+      parser->omp_metadirective_state = old_state;
+    }
+
+  /* Try to resolve the metadirective early.  */
+  if (!processing_template_decl)
+    {
+      candidates = omp_early_resolve_metadirective (ret);
+      if (!candidates.is_empty ())
+       ret = c_omp_expand_variant_construct (candidates);
+    }
+
+  add_stmt (ret);
+  return;
+
+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, true);
+}
+
+
 /* 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
@@ -52862,6 +53360,10 @@ cp_parser_pragma (cp_parser *parser, enum 
pragma_context context, bool *if_p)
       cp_parser_omp_nothing (parser, pragma_tok);
       return false;
 
+    case PRAGMA_OMP_METADIRECTIVE:
+      cp_parser_omp_metadirective (parser, pragma_tok, if_p);
+      return true;
+
     case PRAGMA_OMP_ERROR:
       return cp_parser_omp_error (parser, pragma_tok, context);
 
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 09b356e5e73..09ff57a7f1b 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -450,6 +450,12 @@ struct GTY(()) cp_parser {
   /* Pointer to state for parsing omp_loops.  Managed by
      cp_parser_omp_for_loop in parser.cc and not used outside that file.  */
   struct omp_for_parse_data * GTY((skip)) omp_for_parse_state;
+
+  /* Non-null only when parsing the body of an OpenMP metadirective.
+     Managed by cp_parser_omp_metadirective in parser.cc and not used
+     outside that file.  */
+  struct omp_metadirective_parse_data * GTY((skip))
+    omp_metadirective_state;
 };
 
 /* In parser.cc  */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 8a91c9ce9f6..2c6027c7879 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18056,6 +18056,86 @@ tsubst_omp_clauses (tree clauses, enum 
c_omp_region_type ort,
   return new_clauses;
 }
 
+/* Like tsubst_copy, but specifically for OpenMP context selectors.  */
+static tree
+tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain,
+                            tree in_decl)
+{
+  tree new_ctx = NULL_TREE;
+  for (tree set = ctx; set; set = TREE_CHAIN (set))
+    {
+      tree selectors = NULL_TREE;
+      for (tree sel = OMP_TSS_TRAIT_SELECTORS (set); sel;
+          sel = TREE_CHAIN (sel))
+       {
+         enum omp_ts_code code = OMP_TS_CODE (sel);
+         tree properties = NULL_TREE;
+         tree score = OMP_TS_SCORE (sel);
+         tree t;
+
+         if (score)
+           {
+             score = tsubst_expr (score, args, complain, in_decl);
+             score = fold_non_dependent_expr (score);
+             if (!value_dependent_expression_p (score)
+                 && !type_dependent_expression_p (score))
+               {
+                 if (!INTEGRAL_TYPE_P (TREE_TYPE (score))
+                     || TREE_CODE (score) != INTEGER_CST)
+                   {
+                     error_at (cp_expr_loc_or_input_loc (score),
+                               "%<score%> argument must "
+                               "be constant integer expression");
+                     score = NULL_TREE;
+                   }
+                 else if (tree_int_cst_sgn (score) < 0)
+                   {
+                     error_at (cp_expr_loc_or_input_loc (score),
+                               "%<score%> argument must be non-negative");
+                     score = NULL_TREE;
+                   }
+               }
+           }
+
+         switch (omp_ts_map[OMP_TS_CODE (sel)].tp_type)
+             {
+             case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR:
+             case OMP_TRAIT_PROPERTY_BOOL_EXPR:
+               t = tsubst_expr (OMP_TP_VALUE (OMP_TS_PROPERTIES (sel)),
+                                args, complain, in_decl);
+               t = fold_non_dependent_expr (t);
+               if (!value_dependent_expression_p (t)
+                   && !type_dependent_expression_p (t)
+                   && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+                 error_at (cp_expr_loc_or_input_loc (t),
+                           "property must be integer expression");
+               else
+                 properties = make_trait_property (NULL_TREE, t, NULL_TREE);
+               break;
+             case OMP_TRAIT_PROPERTY_CLAUSE_LIST:
+               if (OMP_TS_CODE (sel) == OMP_TRAIT_CONSTRUCT_SIMD)
+                 properties = tsubst_omp_clauses (OMP_TS_PROPERTIES (sel),
+                                                  C_ORT_OMP_DECLARE_SIMD,
+                                                  args, complain, in_decl);
+               break;
+             default:
+               /* Nothing to do here, just copy.  */
+               for (tree prop = OMP_TS_PROPERTIES (sel);
+                    prop; prop = TREE_CHAIN (prop))
+                 properties = make_trait_property (OMP_TP_NAME (prop),
+                                                   OMP_TP_VALUE (prop),
+                                                   properties);
+             }
+         selectors = make_trait_selector (code, score, properties, selectors);
+       }
+      new_ctx = make_trait_set_selector (OMP_TSS_CODE (set),
+                                        nreverse (selectors),
+                                        new_ctx);
+    }
+  return nreverse (new_ctx);
+}
+
+
 /* Like tsubst_expr, but unshare TREE_LIST nodes.  */
 
 static tree
@@ -19661,6 +19741,52 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
        }
       break;
 
+    case OMP_METADIRECTIVE:
+      {
+       tree variants = NULL_TREE;
+       for (tree v = OMP_METADIRECTIVE_VARIANTS (t); v; v = TREE_CHAIN (v))
+         {
+           tree ctx = OMP_METADIRECTIVE_VARIANT_SELECTOR (v);
+           tree directive = OMP_METADIRECTIVE_VARIANT_DIRECTIVE (v);
+           tree body = OMP_METADIRECTIVE_VARIANT_BODY (v);
+           tree s;
+
+           /* CTX is null if this is the default variant.  */
+           if (ctx)
+             {
+               ctx = tsubst_omp_context_selector (ctx, args, complain,
+                                                  in_decl);
+               /* Remove the selector from further consideration if it can be
+                  evaluated as a non-match at this point.  */
+               if (omp_context_selector_matches (ctx, NULL_TREE, false) == 0)
+                 continue;
+             }
+           s = push_stmt_list ();
+           RECUR (directive);
+           directive = pop_stmt_list (s);
+           if (body)
+             {
+               s = push_stmt_list ();
+               RECUR (body);
+               body = pop_stmt_list (s);
+             }
+           variants
+             = chainon (variants,
+                        make_omp_metadirective_variant (ctx, directive,
+                                                        body));
+         }
+       t = copy_node (t);
+       OMP_METADIRECTIVE_VARIANTS (t) = variants;
+
+       /* Try to resolve the metadirective early.  */
+       vec<struct omp_variant> candidates
+         = omp_early_resolve_metadirective (t);
+       if (!candidates.is_empty ())
+         t = c_omp_expand_variant_construct (candidates);
+       add_stmt (t);
+       break;
+      }
+
     case TRANSACTION_EXPR:
       {
        int flags = 0;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 8dc687f1001..36f348aff00 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4606,7 +4606,8 @@ finish_id_expression_1 (tree id_expression,
       if (TREE_CODE (decl) == PARM_DECL
          && DECL_CONTEXT (decl) == NULL_TREE
          && !cp_unevaluated_operand
-         && !processing_contract_condition)
+         && !processing_contract_condition
+         && !processing_omp_trait_property_expr)
        {
          *error_msg = G_("use of parameter outside function body");
          return error_mark_node;
diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c 
b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
index 13b0614cac4..27c1cdff044 100644
--- a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
@@ -38,9 +38,7 @@ void f18 (void);
 void f19 (void);
 #pragma omp declare variant (f1) match(user={condition()})     /* { dg-error 
"expected \[^\n\r]*expression before '\\)' token" } */
 void f20 (void);
-#pragma omp declare variant (f1) match(user={condition(f1)})   /* { dg-error 
"property must be integer expression" "" { target c } } */
-/* { dg-error "cannot appear in a constant-expression" "" { target c++98_only 
} .-1 } */
-/* { dg-error "property must be constant integer expression" "" { target { 
c++11 } } .-2 } */
+#pragma omp declare variant (f1) match(user={condition(f1)})   /* { dg-error 
"property must be integer expression" } */
 void f21 (void);
 #pragma omp declare variant (f1) match(user={condition(1, 2, 3)})      /* { 
dg-error "expected '\\)' before ',' token" } */
 void f22 (void);
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C 
b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C
new file mode 100644
index 00000000000..35b25de860f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-1.C
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable" } */
+/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* 
x86_64-*-* } } } */
+
+/* References to function parameters in dynamic selector expressions for
+   "declare variant" isn't supported yet; see PR 113904.  Check to see that
+   a proper error is diagnosed meanwhile and GCC doesn't just wander off
+   into the weeds and ICE.  */
+
+extern int frob (int);
+
+class junk 
+{
+ public:
+  int data;
+  static void f01 (int, int);
+  static void f02 (int, int);
+  static void f03 (int, int);
+#pragma omp declare variant (f01) match (target_device={device_num (devnum), 
isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to 
function parameter" } */
+#pragma omp declare variant (f02) match 
(implementation={vendor(score(15):gnu)})
+#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 
42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" 
} */
+  static void f04 (int devnum, int ok);
+};
+
+void
+test1 (void)
+{
+  int i;
+  #pragma omp parallel for
+  for (i = 0; i < 1; i++)
+    junk::f04 (17, 1);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C 
b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C
new file mode 100644
index 00000000000..b30243f70c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-class-2.C
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable" } */
+/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* 
x86_64-*-* } } } */
+
+/* References to function parameters in dynamic selector expressions for
+   "declare variant" isn't supported yet; see PR 113904.  Check to see that
+   a proper error is diagnosed meanwhile and GCC doesn't just wander off
+   into the weeds and ICE.  */
+
+extern int frob (int);
+extern int frobmore (class junk *);
+
+class junk 
+{
+ public:
+  int data;
+  void f01 (int, int);
+  void f02 (int, int);
+  void f03 (int, int);
+  void f04 (int, int);
+  void f05 (int, int);
+#pragma omp declare variant (f01) match (target_device={device_num (devnum), 
isa("avx512f","avx512vl")}) /* { dg-message "sorry, unimplemented: reference to 
function parameter" } */
+#pragma omp declare variant (f02) match 
(implementation={vendor(score(15):gnu)})
+#pragma omp declare variant (f03) match (user={condition(score(11):frob (ok + 
42))}) /* { dg-message "sorry, unimplemented: reference to function parameter" 
} */
+#pragma omp declare variant (f04) match (user={condition(score(11):data)}) /* 
{ dg-message "sorry, unimplemented: reference to function parameter" } */
+#pragma omp declare variant (f05) match (user={condition(score(11):frobmore 
(this))}) /* { dg-message "sorry, unimplemented: reference to function 
parameter" } */
+  void f06 (int devnum, int ok);
+};
+
+void
+test1 (junk *j)
+{
+  int i;
+  #pragma omp parallel for
+  for (i = 0; i < 1; i++)
+    j->f06 (17, 1);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C 
b/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C
new file mode 100644
index 00000000000..92e72baf1c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/metadirective-template-1.C
@@ -0,0 +1,74 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-gimple" }
+
+// Check that expressions in metadirectives are handled correctly in template
+// functions when they are value-dependent and/or type-dependent.
+
+int x;
+
+// Type-dependent expression.
+template <typename T>
+void
+f1 (void)
+{
+#pragma omp metadirective                      \
+  when (user={condition(static_cast<T> (1.0))}: target)
+  x = 1;
+}
+
+// Value-dependent expression.
+template <int N>
+void
+f2 (void)
+{
+#pragma omp metadirective                      \
+  when (user={condition(N)}: target)
+  x = 2;
+}
+
+// Both type- and value-dependent.
+template <typename T, T N>
+void
+f3 (void)
+{
+#pragma omp metadirective                      \
+  when (user={condition(N)}: target)
+  x = 3;
+}
+
+// Expression is itself a template instantiation.
+template <typename T, T N>
+bool
+test (void)
+{
+  return N != 0;
+}
+
+template <typename T, T N>
+void
+f4 (void)
+{
+#pragma omp metadirective                      \
+  when (user={condition(test<T, N> ())}: teams)
+  x = 4;
+}
+
+int
+main (void)
+{
+  f1 <int> ();
+  f2 <1> ();
+  f2 <0> ();
+  f3 <int, 1> ();
+  f3 <int, 0> ();
+  f4 <int, 1> ();
+  f4 <int, 0> ();
+}
+
+// Each of f1..f3 should be instantiated once with a condition expression
+// that is a constant 1, producing a target construct.  The metadirective in
+// f4 has a non-constant condition expression, so both instantiations will
+// produce a conditional including an alternative including a teams construct.
+
+// { dg-final { scan-tree-dump-times "pragma omp target" 3 "gimple" } }
+// { dg-final { scan-tree-dump-times "pragma omp teams" 2 "gimple" } }
diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-1.C 
b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C
new file mode 100644
index 00000000000..45115366522
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/metadirective-template-1.C
@@ -0,0 +1,39 @@
+#include <stdio.h>
+
+template <bool tasking>
+int
+fib (int n)
+{
+  int i, j;
+  if (n < 2)
+    return n;
+  else if ( tasking && n < 8 )  // serial/taskless cutoff for n<8
+    return fib<false> (n);
+  else
+    {
+#pragma omp metadirective                              \
+  when (user = {condition (tasking)}: task shared(i))
+      i = fib<tasking> (n - 1);
+#pragma omp metadirective                                      \
+  when (user = {condition (score(10): tasking)}: task shared(j))
+      j = fib<tasking> (n - 2);
+#pragma omp metadirective                      \
+  when (user = {condition (tasking)}: taskwait)
+      return i + j;
+    }
+}
+
+int
+main ()
+{
+  int n = 15, o = 610;
+#pragma omp parallel
+#pragma omp single
+  {
+    if (fib<true> (n) != o)
+      __builtin_abort ();
+    if (fib<false> (n) != o)
+      __builtin_abort ();
+  }
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-2.C 
b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C
new file mode 100644
index 00000000000..a116bfd6f61
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/metadirective-template-2.C
@@ -0,0 +1,43 @@
+# include <stdio.h>
+
+template <bool tasking>
+int
+fib (int n, bool flag)
+{
+  int i, j;
+  if (n < 2)
+    return n;
+  else if ( tasking && flag && n < 8 )  // serial/taskless cutoff for n<8
+    return fib<false> (n, false);
+  else
+    {
+#pragma omp metadirective                              \
+  when (user = {condition (tasking && flag)}: task shared(i))
+      i = fib<tasking> (n - 1, flag);
+#pragma omp metadirective                                      \
+  when (user = {condition (score(10): tasking && flag)}: task shared(j))
+      j = fib<tasking> (n - 2, flag);
+#pragma omp metadirective                      \
+  when (user = {condition (tasking && flag)}: taskwait)
+      return i + j;
+    }
+}
+
+int
+main ()
+{
+  int n = 15, o = 610;
+#pragma omp parallel
+#pragma omp single
+  {
+    if (fib<true> (n, true) != o)
+      __builtin_abort ();
+    if (fib<true> (n, false) != o)
+      __builtin_abort ();
+    if (fib<false> (n, false) != o)
+      __builtin_abort ();
+    if (fib<false> (n, true) != o)
+      __builtin_abort ();
+  }
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/metadirective-template-3.C 
b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C
new file mode 100644
index 00000000000..934ae499c00
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/metadirective-template-3.C
@@ -0,0 +1,43 @@
+# include <stdio.h>
+
+template <bool tasking>
+int
+fib (int n, bool flag)
+{
+  int i, j;
+  if (n < 2)
+    return n;
+  else if ( tasking && flag && n < 8 )  // serial/taskless cutoff for n<8
+    return fib<false> (n, false);
+  else
+    {
+#pragma omp metadirective                              \
+  when (user = {condition (tasking && flag)}: task shared(i))  \
+  when (user = {condition (!tasking && !flag)}: nothing) \
+  otherwise (error at(execution) message("oops 1"))
+      i = fib<tasking> (n - 1, flag);
+#pragma omp metadirective                                      \
+  when (user = {condition (score(10): tasking && flag)}: task shared(j)) \
+  when (user = {condition (tasking || flag)} : \
+         error at(execution) message ("oops 2"))
+      j = fib<tasking> (n - 2, flag);
+#pragma omp metadirective                      \
+  when (user = {condition (tasking && flag)}: taskwait)
+      return i + j;
+    }
+}
+
+int
+main ()
+{
+  int n = 15, o = 610;
+#pragma omp parallel
+#pragma omp single
+  {
+    if (fib<true> (n, true) != o)
+      __builtin_abort ();
+    if (fib<false> (n, false) != o)
+      __builtin_abort ();
+  }
+  return 0;
+}
-- 
2.25.1

Reply via email to