openmp: Diagnose invalid mixing of the attribute and pragma syntax directives
authorJakub Jelinek <jakub@redhat.com>
Fri, 23 Jul 2021 07:37:36 +0000 (09:37 +0200)
committerJakub Jelinek <jakub@redhat.com>
Fri, 23 Jul 2021 07:37:36 +0000 (09:37 +0200)
The OpenMP 5.1 spec says that the attribute and pragma syntax directives
should not be mixed on the same statement.  The following patch adds diagnostic
for that,
  [[omp::directive (...)]]
  #pragma omp ...
is always an error and for the other order
  #pragma omp ...
  [[omp::directive (...)]]
it depends on whether the pragma directive is an OpenMP construct
(then it is an error because it needs a structured block or loop
or statement as body) or e.g. a standalone directive (then it is fine).

Only block scope is handled for now though, namespace scope and class scope
still needs implementing even the basic support.

2021-07-23  Jakub Jelinek  <jakub@redhat.com>

gcc/c-family/
* c-pragma.h (enum pragma_kind): Add PRAGMA_OMP__START_ and
PRAGMA_OMP__LAST_ enumerators.
gcc/cp/
* parser.h (struct cp_parser): Add omp_attrs_forbidden_p member.
* parser.c (cp_parser_handle_statement_omp_attributes): Diagnose
mixing of attribute and pragma syntax directives when seeing
omp::directive if parser->omp_attrs_forbidden_p or if attribute syntax
directives are followed by OpenMP pragma.
(cp_parser_statement): Clear parser->omp_attrs_forbidden_p after
the cp_parser_handle_statement_omp_attributes call.
(cp_parser_omp_structured_block): Add disallow_omp_attrs argument,
if true, set parser->omp_attrs_forbidden_p.
(cp_parser_omp_scan_loop_body, cp_parser_omp_sections_scope): Pass
false as disallow_omp_attrs to cp_parser_omp_structured_block.
(cp_parser_omp_parallel, cp_parser_omp_task): Set
parser->omp_attrs_forbidden_p.
gcc/testsuite/
* g++.dg/gomp/attrs-4.C: New test.
* g++.dg/gomp/attrs-5.C: New test.

gcc/c-family/c-pragma.h
gcc/cp/parser.c
gcc/cp/parser.h
gcc/testsuite/g++.dg/gomp/attrs-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/attrs-5.C [new file with mode: 0644]

index c5d11ce..abd6667 100644 (file)
@@ -42,7 +42,9 @@ enum pragma_kind {
   PRAGMA_OACC_UPDATE,
   PRAGMA_OACC_WAIT,
 
+  /* PRAGMA_OMP__START_ should be equal to the first PRAGMA_OMP_* code.  */
   PRAGMA_OMP_ALLOCATE,
+  PRAGMA_OMP__START_ = PRAGMA_OMP_ALLOCATE,
   PRAGMA_OMP_ATOMIC,
   PRAGMA_OMP_BARRIER,
   PRAGMA_OMP_CANCEL,
@@ -72,6 +74,8 @@ enum pragma_kind {
   PRAGMA_OMP_TASKYIELD,
   PRAGMA_OMP_THREADPRIVATE,
   PRAGMA_OMP_TEAMS,
+  /* PRAGMA_OMP__LAST_ should be equal to the last PRAGMA_OMP_* code.  */
+  PRAGMA_OMP__LAST_ = PRAGMA_OMP_TEAMS,
 
   PRAGMA_GCC_PCH_PREPROCESS,
   PRAGMA_IVDEP,
index 45216f0..18905cf 100644 (file)
@@ -11665,6 +11665,7 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
   auto_vec<cp_omp_attribute_data, 16> vec;
   int cnt = 0;
   int tokens = 0;
+  bool bad = false;
   for (tree *pa = &attrs; *pa; )
     if (get_attribute_namespace (*pa) == omp_identifier
        && is_attribute_p ("directive", get_attribute_name (*pa)))
@@ -11676,6 +11677,14 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
            gcc_assert (TREE_CODE (d) == DEFERRED_PARSE);
            cp_token *first = DEFPARSE_TOKENS (d)->first;
            cp_token *last = DEFPARSE_TOKENS (d)->last;
+           if (parser->omp_attrs_forbidden_p)
+             {
+               error_at (first->location,
+                         "mixing OpenMP directives with attribute and pragma "
+                         "syntax on the same statement");
+               parser->omp_attrs_forbidden_p = false;
+               bad = true;
+             }
            const char *directive[3] = {};
            for (int i = 0; i < 3; i++)
              {
@@ -11731,6 +11740,9 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
     else
       pa = &TREE_CHAIN (*pa);
 
+  if (bad)
+    return attrs;
+
   unsigned int i;
   cp_omp_attribute_data *v;
   cp_omp_attribute_data *construct_seen = nullptr;
@@ -11780,6 +11792,18 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
                " can only appear on an empty statement");
       return attrs;
     }
+  if (cnt && cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA))
+    {
+      cp_token *token = cp_lexer_peek_token (parser->lexer);
+      enum pragma_kind kind = cp_parser_pragma_kind (token);
+      if (kind >= PRAGMA_OMP__START_ && kind <= PRAGMA_OMP__LAST_)
+       {
+         error_at (token->location,
+                   "mixing OpenMP directives with attribute and pragma "
+                   "syntax on the same statement");
+         return attrs;
+       }
+    }
 
   if (!tokens)
     return attrs;
@@ -11904,6 +11928,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
 
   if (std_attrs && (flag_openmp || flag_openmp_simd))
     std_attrs = cp_parser_handle_statement_omp_attributes (parser, std_attrs);
+  parser->omp_attrs_forbidden_p = false;
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
@@ -39391,11 +39416,14 @@ cp_parser_end_omp_structured_block (cp_parser *parser, unsigned save)
 }
 
 static tree
-cp_parser_omp_structured_block (cp_parser *parser, bool *if_p)
+cp_parser_omp_structured_block (cp_parser *parser, bool *if_p,
+                               bool disallow_omp_attrs = true)
 {
   tree stmt = begin_omp_structured_block ();
   unsigned int save = cp_parser_begin_omp_structured_block (parser);
 
+  if (disallow_omp_attrs)
+    parser->omp_attrs_forbidden_p = true;
   cp_parser_statement (parser, NULL_TREE, false, if_p);
 
   cp_parser_end_omp_structured_block (parser, save);
@@ -40761,7 +40789,7 @@ cp_parser_omp_scan_loop_body (cp_parser *parser)
   if (!braces.require_open (parser))
     return;
 
-  substmt = cp_parser_omp_structured_block (parser, NULL);
+  substmt = cp_parser_omp_structured_block (parser, NULL, false);
   substmt = build2 (OMP_SCAN, void_type_node, substmt, NULL_TREE);
   add_stmt (substmt);
 
@@ -40796,7 +40824,7 @@ cp_parser_omp_scan_loop_body (cp_parser *parser)
     error ("expected %<#pragma omp scan%>");
 
   clauses = finish_omp_clauses (clauses, C_ORT_OMP);
-  substmt = cp_parser_omp_structured_block (parser, NULL);
+  substmt = cp_parser_omp_structured_block (parser, NULL, false);
   substmt = build2_loc (tok->location, OMP_SCAN, void_type_node, substmt,
                        clauses);
   add_stmt (substmt);
@@ -41597,7 +41625,7 @@ cp_parser_omp_sections_scope (cp_parser *parser)
   if (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
       != PRAGMA_OMP_SECTION)
     {
-      substmt = cp_parser_omp_structured_block (parser, NULL);
+      substmt = cp_parser_omp_structured_block (parser, NULL, false);
       substmt = build1 (OMP_SECTION, void_type_node, substmt);
       add_stmt (substmt);
     }
@@ -41622,7 +41650,7 @@ cp_parser_omp_sections_scope (cp_parser *parser)
          error_suppress = true;
        }
 
-      substmt = cp_parser_omp_structured_block (parser, NULL);
+      substmt = cp_parser_omp_structured_block (parser, NULL, false);
       substmt = build1 (OMP_SECTION, void_type_node, substmt);
       add_stmt (substmt);
     }
@@ -41842,6 +41870,7 @@ cp_parser_omp_parallel (cp_parser *parser, cp_token *pragma_tok,
 
   block = begin_omp_parallel ();
   save = cp_parser_begin_omp_structured_block (parser);
+  parser->omp_attrs_forbidden_p = true;
   cp_parser_statement (parser, NULL_TREE, false, if_p);
   cp_parser_end_omp_structured_block (parser, save);
   stmt = finish_omp_parallel (clauses, block);
@@ -41904,6 +41933,7 @@ cp_parser_omp_task (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
                                       "#pragma omp task", pragma_tok);
   block = begin_omp_task ();
   save = cp_parser_begin_omp_structured_block (parser);
+  parser->omp_attrs_forbidden_p = true;
   cp_parser_statement (parser, NULL_TREE, false, if_p);
   cp_parser_end_omp_structured_block (parser, save);
   return finish_omp_task (clauses, block);
index 5ef7047..6fdd214 100644 (file)
@@ -398,6 +398,9 @@ struct GTY(()) cp_parser {
      identifiers) rather than an explicit template parameter list.  */
   bool fully_implicit_function_template_p;
 
+  /* TRUE if omp::directive or omp::sequence attributes may not appear.  */
+  bool omp_attrs_forbidden_p;
+
   /* Tracks the function's template parameter list when declaring a function
      using generic type parameters.  This is either a new chain in the case of a
      fully implicit function template or an extension of the function's existing
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-4.C b/gcc/testsuite/g++.dg/gomp/attrs-4.C
new file mode 100644 (file)
index 0000000..005add8
--- /dev/null
@@ -0,0 +1,61 @@
+// { dg-do compile { target c++11 } }
+
+void
+foo (int x)
+{
+  [[omp::directive (parallel)]]
+  #pragma omp for                                              // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  for (int i = 0; i < 16; i++)
+    ;
+  [[omp::directive (barrier)]]                                 // { dg-error "standalone OpenMP directives in 'omp::directive' attribute can only appear on an empty statement" }
+  #pragma omp flush
+  ;
+  #pragma omp parallel
+  [[omp::directive (master)]]                                  // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp teams
+  [[omp::sequence (directive (parallel), directive (master))]] // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp task
+  [[omp::directive (flush)]]                                   // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp master
+  [[omp::directive (flush)]]                                   // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp for ordered
+  for (int i = 0; i < 16; i++)
+    #pragma omp ordered
+    [[omp::directive (flush)]]                                 // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+    ;
+  #pragma omp single
+  [[omp::directive (flush)]]                                   // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp taskgroup
+  [[omp::directive (taskyield)]]                               // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp target data map (tofrom: x)
+  [[omp::directive (flush)]]                                   // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp target
+  [[omp::directive (teams)]]                                   // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  [[omp::directive (parallel)]]
+  #pragma omp master                                           // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  [[omp::sequence (omp::directive (taskloop))]]                        // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  for (int i = 0; i < 16; i++)
+    ;
+  #pragma omp parallel
+  [[omp::directive (for)]]                                     // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  for (int i = 0; i < 16; i++)
+    ;
+  #pragma omp for
+  [[omp::directive (master)]]                                  // { dg-error "for statement expected before '\\\[' token" }
+  ;
+  #pragma omp target teams
+  [[omp::directive (parallel)]]                                        // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  ;
+  #pragma omp parallel master
+  [[omp::directive (taskloop)]]                                        // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
+  for (int i = 0; i < 16; i++)
+    ;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-5.C b/gcc/testsuite/g++.dg/gomp/attrs-5.C
new file mode 100644 (file)
index 0000000..f6d24b9
--- /dev/null
@@ -0,0 +1,46 @@
+// { dg-do compile { target c++11 } }
+
+typedef struct __attribute__((__aligned__ (sizeof (void *)))) omp_depend_t {
+  char __omp_depend_t__[2 * sizeof (void *)];
+} omp_depend_t;
+
+void
+foo (int x)
+{
+  #pragma omp barrier
+  [[omp::directive (barrier)]];
+  #pragma omp parallel
+  {
+    #pragma omp cancel parallel
+    [[omp::directive (cancellation point, parallel)]];
+  }
+  #pragma omp parallel
+  {
+    #pragma omp cancellation point parallel
+    [[omp::directive (cancel parallel)]];
+  }
+  #pragma omp parallel
+  {
+    [[omp::directive (cancel, parallel)]];
+    #pragma omp cancellation point parallel
+  }
+  omp_depend_t depobj;
+  #pragma omp depobj(depobj) update(inout)
+  [[omp::directive (depobj(depobj), destroy)]];
+  #pragma omp flush
+  [[omp::directive (flush)]];
+  #pragma omp target enter data map (to: x)
+  [[omp::directive (target exit data, map (from: x))]];
+  [[omp::directive (target enter data, map (to: x))]];
+  #pragma omp target exit data map (from: x)
+  [[omp::directive (flush)]];
+  #pragma omp target update to (x)
+  [[omp::directive (flush)]];
+  #pragma omp taskwait
+  [[omp::directive (flush)]];
+  #pragma omp taskyield
+  [[omp::directive (flush)]];
+  extern int t;
+  #pragma omp threadprivate (t)
+  [[omp::directive (flush)]];
+}