[committed] openmp: Diagnose invalid mixing of the attribute and pragma syntax directives

Message ID 20210723080925.GZ2380545@tucnak
State New
Headers show
Series
  • [committed] openmp: Diagnose invalid mixing of the attribute and pragma syntax directives
Related show

Commit Message

Andrew Pinski via Gcc-patches July 23, 2021, 8:09 a.m.
Hi!

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.

Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.

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.



	Jakub

Patch

--- gcc/c-family/c-pragma.h.jj	2021-07-22 12:37:20.409533286 +0200
+++ gcc/c-family/c-pragma.h	2021-07-22 12:44:57.903028283 +0200
@@ -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,
--- gcc/cp/parser.h.jj	2021-07-02 21:59:12.350171752 +0200
+++ gcc/cp/parser.h	2021-07-22 15:26:41.905013091 +0200
@@ -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
--- gcc/cp/parser.c.jj	2021-07-22 12:37:20.445532774 +0200
+++ gcc/cp/parser.c	2021-07-22 17:47:26.025761491 +0200
@@ -11665,6 +11665,7 @@  cp_parser_handle_statement_omp_attribute
   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_attribute
 	    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_attribute
     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_attribute
 		" 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,
 
   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_p
 }
 
 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
   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
     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
   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
 	  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 *parse
 
   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, c
 				       "#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);
--- gcc/testsuite/g++.dg/gomp/attrs-4.C.jj	2021-07-22 16:32:23.295177963 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-4.C	2021-07-22 18:34:59.823183344 +0200
@@ -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++)
+    ;
+}
--- gcc/testsuite/g++.dg/gomp/attrs-5.C.jj	2021-07-22 18:30:03.934285592 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-5.C	2021-07-22 18:31:39.517960417 +0200
@@ -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)]];
+}