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 <k...@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  <k...@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

Reply via email to