[committed] openmp: Add support for omp::directive and omp::sequence attributes in C2X

Message ID ZUX9vqowoNJhBq3L@tucnak
State Unresolved
Headers
Series [committed] openmp: Add support for omp::directive and omp::sequence attributes in C2X |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek Nov. 4, 2023, 8:15 a.m. UTC
  Hi!

The following patch adds support for attribute syntax which we have in C++11
and above since GCC 12 also for C, where OpenMP standard is going to add it
in OpenMP 6.0.

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

2023-11-04  Jakub Jelinek  <jakub@redhat.com>

gcc/c/
	* c-tree.def: New file.
	* c-tree.h (struct c_tree_token_vec): Forward declare.
	(c_tree_size): Declare.
	* c-lang.h (struct c_omp_declare_target_attr): Add attr_syntax member.
	(struct c_omp_begin_assumes_data): New type.
	(current_omp_begin_assumes): Change type from int to
	vec<c_omp_begin_assumes_data, va_gc> *.
	* c-lang.cc: Include c-family/c-pragma.h and c-parser.h.
	* c-parser.h (struct c_tree_token_vec_struct): New type.
	(C_TOKEN_VEC_TOKENS): New macro.
	* c-parser.cc (struct c_parser): Add omp_attrs_forbidden_p and
	in_omp_attribute_pragma members.
	(c_parser_skip_until_found): Handle CPP_PRAGMA_EOL when
	parser->in_omp_attribute_pragma.
	(c_parser_skip_to_pragma_eol): Likewise.
	(c_parser_translation_unit): Adjust for current_omp_begin_assumes
	being a vector rather than counter.
	(c_parser_declaration_or_fndef): Handle omp::directive and
	omp::sequence attributes on attribute declaration and declare simd
	or declare variant directives in those on function declarations.
	(c_parser_check_balanced_raw_token_sequence): Forward declare.
	(c_parser_omp_directive_args, c_parser_omp_sequence_args): New
	functions.
	(c_parser_std_attribute): Handle omp::directive and omp::sequence
	attributes.
	(struct c_omp_attribute_data): New type.
	(c_parser_handle_statement_omp_attributes,
	c_parser_handle_directive_omp_attributes): New functions.
	(c_parser_compound_statement_nostart): Handle omp::directive and
	omp::sequence attributes on statements.  Formatting fix.
	(c_parser_all_labels): Handle omp::directive and omp::sequence
	attributes on statements.
	(c_parser_statement): Clear parser->omp_attrs_forbidden_p.
	(c_parser_omp_variable_list): Handle parser->tokens
	!= &parser->tokens_buf[0] by saving/restoring it.
	(c_parser_omp_structured_block): Set parser->omp_attrs_forbidden_p.
	(c_parser_omp_section_scan): New function.
	(c_parser_omp_structured_block_sequence, c_parser_omp_sections_scope):
	Use it.
	(c_parser_omp_parallel): Set parser->omp_attrs_forbidden_p.
	(c_parser_omp_task): Likewise.
	(c_parser_omp_declare_simd): Handle function declaration after
	std attributes.
	(c_finish_omp_declare_simd): Don't assert all kinds are the same.
	(c_parser_omp_declare_target): Also push attr_syntax flag.
	(c_parser_omp_begin): Likewise.  Adjust for current_omp_begin_assumes
	type change.
	(c_parser_omp_end): Adjust for current_omp_begin_assumes type
	change.  Diagnose mixing of attribute vs. pragma syntax on end assumes
	or end declare target.
	(c_parser_omp_declare_reduction): Handle parser->tokens
	!= &parser->tokens_buf[0] by saving/restoring it.
	* c-decl.cc: Include c-parser.h.
	(current_omp_begin_assumes): Change type from int to
	vec<c_omp_begin_assumes_data, va_gc> *.
	(struct c_tree_token_vec): New type.  Add static assertions
	for sizeof and offsetof.
	(union lang_tree_node): Add c_token_vec member and adjust GTY
	desc for it.
	(c_tree_size): New function.
	(c_decl_attributes): Diagnose invalid omp::directive attribute
	uses.
	* c-objc-common.h (LANG_HOOKS_TREE_SIZE): Redefine.
gcc/cp/
	* parser.h (struct cp_parser): Adjust comment on omp_attrs_forbidden_p
	member.
	* parser.cc (cp_parser_omp_section_scan): Allow __directive__ spelling.
gcc/objc/
	* objc-act.h (objc_common_tree_size): Remove.
	* objc-act.cc (objc_common_tree_size): Remove.
	* objc-lang.cc (LANG_HOOKS_TREE_SIZE): Remove.
gcc/testsuite/
	* gcc.dg/gomp/attrs-1.c: New test.
	* gcc.dg/gomp/attrs-2.c: New test.
	* gcc.dg/gomp/attrs-3.c: New test.
	* gcc.dg/gomp/attrs-4.c: New test.
	* gcc.dg/gomp/attrs-5.c: New test.
	* gcc.dg/gomp/attrs-6.c: New test.
	* gcc.dg/gomp/attrs-7.c: New test.
	* gcc.dg/gomp/attrs-8.c: New test.
	* gcc.dg/gomp/attrs-9.c: New test.
	* gcc.dg/gomp/attrs-10.c: New test.
	* gcc.dg/gomp/attrs-11.c: New test.
	* gcc.dg/gomp/attrs-12.c: New test.
	* gcc.dg/gomp/attrs-13.c: New test.
	* gcc.dg/gomp/attrs-14.c: New test.
	* gcc.dg/gomp/attrs-15.c: New test.
	* gcc.dg/gomp/attrs-16.c: New test.
	* gcc.dg/gomp/attrs-17.c: New test.
	* gcc.dg/gomp/attrs-18.c: New test.
	* g++.dg/gomp/attrs-2.C: Enable for c++11 rather than just
	c++17.  Avoid using omp : syntax for c++11, c++14 and c.


	Jakub
  

Patch

--- gcc/c/c-tree.def.jj	2023-11-02 12:21:36.558631526 +0100
+++ gcc/c/c-tree.def	2023-11-02 12:21:36.558631526 +0100
@@ -0,0 +1,31 @@ 
+/* This file contains the definitions and documentation for the
+   additional tree codes used in the GNU C compiler (see tree.def
+   for the standard codes).
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Tree nodes used in the C frontend only, not shared with C++ frontend.  */
+
+/* Used to represent a vector of tokens for deferred parsing.  */
+DEFTREECODE (C_TOKEN_VEC, "c_token_vec", tcc_exceptional, 0)
+
+/*
+Local variables:
+mode:c
+End:
+*/
--- gcc/c/c-tree.h.jj	2023-11-02 07:48:27.682553518 +0100
+++ gcc/c/c-tree.h	2023-11-02 12:21:36.558631526 +0100
@@ -596,6 +596,7 @@  enum c_inline_static_type {
 
 
 /* in c-parser.cc */
+struct c_tree_token_vec;
 extern void c_parse_init (void);
 extern bool c_keyword_starts_typename (enum rid keyword);
 
@@ -719,6 +720,7 @@  extern struct c_declspecs *declspecs_add
 extern struct c_declspecs *declspecs_add_alignas (location_t,
 						  struct c_declspecs *, tree);
 extern struct c_declspecs *finish_declspecs (struct c_declspecs *);
+extern size_t c_tree_size (enum tree_code);
 
 /* in c-objc-common.cc */
 extern bool c_objc_common_init (void);
--- gcc/c/c-lang.h.jj	2023-11-02 07:48:27.556555272 +0100
+++ gcc/c/c-lang.h	2023-11-02 12:31:57.730957162 +0100
@@ -61,15 +61,21 @@  struct GTY(()) language_function {
 };
 
 struct GTY(()) c_omp_declare_target_attr {
+  bool attr_syntax;
   int device_type;
 };
 
-/* If non-zero, implicit "omp declare target" attribute is added into the
+struct GTY(()) c_omp_begin_assumes_data {
+  bool attr_syntax;
+};
+
+/* If non-empty, implicit "omp declare target" attribute is added into the
    attribute lists.  */
 extern GTY(()) vec<c_omp_declare_target_attr, va_gc>
   *current_omp_declare_target_attribute;
 /* Similarly whether we are in between #pragma omp begin assumes and
    #pragma omp end assumes (and how many times when nested).  */
-extern GTY(()) int current_omp_begin_assumes;
+extern GTY(()) vec<c_omp_begin_assumes_data, va_gc>
+  *current_omp_begin_assumes;
 
 #endif /* ! GCC_C_LANG_H */
--- gcc/c/c-lang.cc.jj	2023-11-02 07:48:27.526555690 +0100
+++ gcc/c/c-lang.cc	2023-11-02 12:21:36.558631526 +0100
@@ -25,6 +25,8 @@  along with GCC; see the file COPYING3.
 #include "langhooks.h"
 #include "langhooks-def.h"
 #include "c-objc-common.h"
+#include "c-family/c-pragma.h"
+#include "c-parser.h"
 
 enum c_language_kind c_language = clk_c;
 
--- gcc/c/c-parser.h.jj	2023-11-02 07:48:27.663553782 +0100
+++ gcc/c/c-parser.h	2023-11-02 17:19:22.159725295 +0100
@@ -80,6 +80,17 @@  struct GTY (()) c_token {
   }
 };
 
+/* This should have the same layout as c_tree_token_vec
+   in c-decl.cc, but for GTY reasons with ObjC can't be
+   GTY itself.  */
+struct c_tree_token_vec_struct {
+  struct tree_base base;
+  vec<c_token, va_gc> *tokens;
+};
+
+#define C_TOKEN_VEC_TOKENS(NODE) \
+  (((struct c_tree_token_vec_struct *) TREE_CHECK (NODE, C_TOKEN_VEC))->tokens)
+
 /* The parser.  */
 struct c_parser;
 
--- gcc/c/c-parser.cc.jj	2023-11-02 07:49:14.405902751 +0100
+++ gcc/c/c-parser.cc	2023-11-02 17:19:40.835466808 +0100
@@ -247,12 +247,21 @@  struct GTY(()) c_parser {
      macro.  */
   BOOL_BITFIELD seen_string_literal : 1;
 
+  /* TRUE if omp::directive, omp::decl or omp::sequence attributes may not
+     appear.  */
+  BOOL_BITFIELD omp_attrs_forbidden_p : 1;
+
   /* Location of the last consumed token.  */
   location_t last_token_location;
 
   /* Holds state for parsing collapsed OMP_FOR loops.  Managed by
      c_parser_omp_for_loop.  */
   struct omp_for_parse_data * GTY((skip)) omp_for_parse_state;
+
+  /* If we're in the context of OpenMP directives written as C23
+     attributes turned into pragma, vector of tokens created from that,
+     otherwise NULL.  */
+  vec<c_token, va_gc> *in_omp_attribute_pragma;
 };
 
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
@@ -1288,7 +1297,19 @@  c_parser_skip_until_found (c_parser *par
   unsigned nesting_depth = 0;
 
   if (c_parser_require (parser, type, msgid, matching_location))
-    return;
+    {
+      if (UNLIKELY (type == CPP_PRAGMA_EOL) && parser->in_omp_attribute_pragma)
+	{
+	  c_token *token = c_parser_peek_token (parser);
+	  if (token->type == CPP_EOF)
+	    {
+	      parser->tokens = &parser->tokens_buf[0];
+	      parser->tokens_avail = token->flags;
+	      parser->in_omp_attribute_pragma = NULL;
+	    }
+	}
+      return;
+    }
 
   /* Skip tokens until the desired token is found.  */
   while (true)
@@ -1299,6 +1320,17 @@  c_parser_skip_until_found (c_parser *par
       if (token->type == type && !nesting_depth)
 	{
 	  c_parser_consume_token (parser);
+	  if (UNLIKELY (type == CPP_PRAGMA_EOL)
+	      && parser->in_omp_attribute_pragma)
+	    {
+	      c_token *token = c_parser_peek_token (parser);
+	      if (token->type == CPP_EOF)
+		{
+		  parser->tokens = &parser->tokens_buf[0];
+		  parser->tokens_avail = token->flags;
+		  parser->in_omp_attribute_pragma = NULL;
+		}
+	    }
 	  break;
 	}
 
@@ -1383,6 +1415,17 @@  c_parser_skip_to_pragma_eol (c_parser *p
     }
   while (token_type != CPP_PRAGMA_EOL);
 
+  if (parser->in_omp_attribute_pragma)
+    {
+      c_token *token = c_parser_peek_token (parser);
+      if (token->type == CPP_EOF)
+	{
+	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens_avail = token->flags;
+	  parser->in_omp_attribute_pragma = NULL;
+	}
+    }
+
   parser->error = false;
 }
 
@@ -1581,6 +1624,8 @@  static void c_parser_declaration_or_fnde
 					   tree attrs = NULL,
 					   struct oacc_routine_data * = NULL,
 					   bool * = NULL);
+static bool c_parser_handle_statement_omp_attributes (c_parser *, tree &,
+						      bool *);
 static void c_parser_static_assert_declaration_no_semi (c_parser *);
 static void c_parser_static_assert_declaration (c_parser *);
 static struct c_typespec c_parser_enum_specifier (c_parser *);
@@ -1871,12 +1916,12 @@  c_parser_translation_unit (c_parser *par
 	       "#pragma omp end declare target");
       vec_safe_truncate (current_omp_declare_target_attribute, 0);
     }
-  if (current_omp_begin_assumes)
+  if (vec_safe_length (current_omp_begin_assumes))
     {
       if (!errorcount)
 	error ("%qs without corresponding %qs",
 	       "#pragma omp begin assumes", "#pragma omp end assumes");
-      current_omp_begin_assumes = 0;
+      vec_safe_truncate (current_omp_begin_assumes, 0);
     }
 
 #if ENABLE_ANALYZER
@@ -1999,6 +2044,8 @@  c_parser_external_declaration (c_parser
     }
 }
 
+static void c_parser_handle_directive_omp_attributes (tree &, vec<c_token> *&,
+						      vec<c_token> *);
 static void c_finish_omp_declare_simd (c_parser *, tree, tree, vec<c_token> *);
 static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool);
 
@@ -2301,6 +2348,22 @@  c_parser_declaration_or_fndef (c_parser
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
     {
       bool handled_assume = false;
+      if (specs->attrs
+	  && !nested
+	  && specs->typespec_kind == ctsk_none
+	  && c_parser_handle_statement_omp_attributes (parser, specs->attrs,
+						       NULL))
+	{
+	  if (specs->attrs)
+	    c_warn_unused_attributes (specs->attrs);
+	  while (parser->in_omp_attribute_pragma)
+	    {
+	      gcc_assert (c_parser_next_token_is (parser, CPP_PRAGMA));
+	      c_parser_pragma (parser, pragma_external, NULL);
+	    }
+	  c_parser_consume_token (parser);
+	  return;
+	}
       if (specs->typespec_kind == ctsk_none
 	  && lookup_attribute ("gnu", "assume", specs->attrs))
 	{
@@ -2440,6 +2503,10 @@  c_parser_declaration_or_fndef (c_parser
     warning_at (here, OPT_Wattributes,
 		"%<assume%> attribute not followed by %<;%>");
 
+  auto_vec<c_token> omp_declare_simd_attr_clauses;
+  c_parser_handle_directive_omp_attributes (specs->attrs,
+					    omp_declare_simd_clauses,
+					    &omp_declare_simd_attr_clauses);
   pending_xref_error ();
   prefix_attrs = specs->attrs;
   all_prefix_attrs = prefix_attrs;
@@ -2451,6 +2518,7 @@  c_parser_declaration_or_fndef (c_parser
       timevar_id_t tv;
       tree fnbody = NULL_TREE;
       tree underspec_name = NULL_TREE;
+      auto_vec<c_token> omp_dsimd_idattr_clauses;
       /* Declaring either one or more declarators (in which case we
 	 should diagnose if there were no declaration specifiers) or a
 	 function definition (in which case the diagnostic for
@@ -2468,6 +2536,15 @@  c_parser_declaration_or_fndef (c_parser
 	  c_parser_skip_to_end_of_block_or_statement (parser);
 	  return;
 	}
+      if (flag_openmp || flag_openmp_simd)
+	{
+	  struct c_declarator *d = declarator;
+	  while (d->kind != cdk_id)
+	    d = d->declarator;
+	  vec<c_token> *dummy = NULL;
+	  c_parser_handle_directive_omp_attributes (d->u.id.attrs, dummy,
+						    &omp_dsimd_idattr_clauses);
+	}
       if (gnu_auto_type_p && declarator->kind != cdk_id)
 	{
 	  error_at (here,
@@ -2607,6 +2684,9 @@  c_parser_declaration_or_fndef (c_parser
 		  if (omp_declare_simd_clauses)
 		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
 					       omp_declare_simd_clauses);
+		  if (!omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       &omp_dsimd_idattr_clauses);
 		}
 	      else
 		{
@@ -2628,6 +2708,10 @@  c_parser_declaration_or_fndef (c_parser
 		  if (!specs->constexpr_p && omp_declare_simd_clauses)
 		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
 					       omp_declare_simd_clauses);
+		  if (!specs->constexpr_p
+		      && !omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       &omp_dsimd_idattr_clauses);
 		  start_init (d, asm_name,
 			      TREE_STATIC (d) || specs->constexpr_p,
 			      specs->constexpr_p, &richloc);
@@ -2646,6 +2730,10 @@  c_parser_declaration_or_fndef (c_parser
 		      if (omp_declare_simd_clauses)
 			c_finish_omp_declare_simd (parser, d, NULL_TREE,
 						   omp_declare_simd_clauses);
+		  if (!specs->constexpr_p
+		      && !omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       &omp_dsimd_idattr_clauses);
 		    }
 		  finish_init ();
 		}
@@ -2715,7 +2803,8 @@  c_parser_declaration_or_fndef (c_parser
 		      warn_parm_array_mismatch (lastloc, d, parms);
 		    }
 		}
-	      if (omp_declare_simd_clauses)
+	      if (omp_declare_simd_clauses
+		  || !omp_dsimd_idattr_clauses.is_empty ())
 		{
 		  tree parms = NULL_TREE;
 		  if (d && TREE_CODE (d) == FUNCTION_DECL)
@@ -2732,8 +2821,13 @@  c_parser_declaration_or_fndef (c_parser
 		    }
 		  if (parms)
 		    temp_store_parm_decls (d, parms);
-		  c_finish_omp_declare_simd (parser, d, parms,
-					     omp_declare_simd_clauses);
+		  if (omp_declare_simd_clauses)
+		    c_finish_omp_declare_simd (parser, d, parms,
+					       omp_declare_simd_clauses);
+		  if (!specs->constexpr_p
+		      && !omp_dsimd_idattr_clauses.is_empty ())
+		    c_finish_omp_declare_simd (parser, d, parms,
+					       &omp_dsimd_idattr_clauses);
 		  if (parms)
 		    temp_pop_parm_decls ();
 		}
@@ -2878,6 +2972,9 @@  c_parser_declaration_or_fndef (c_parser
       if (omp_declare_simd_clauses)
 	c_finish_omp_declare_simd (parser, current_function_decl, NULL_TREE,
 				   omp_declare_simd_clauses);
+      if (!omp_dsimd_idattr_clauses.is_empty ())
+	c_finish_omp_declare_simd (parser, current_function_decl, NULL_TREE,
+				   &omp_dsimd_idattr_clauses);
       if (oacc_routine_data)
 	c_finish_oacc_routine (oacc_routine_data, current_function_decl, true);
       location_t startloc = c_parser_peek_token (parser)->location;
@@ -5430,6 +5527,134 @@  c_parser_balanced_token_sequence (c_pars
     }
 }
 
+static bool c_parser_check_balanced_raw_token_sequence (c_parser *,
+							unsigned int *);
+
+/* Parse arguments of omp::directive or omp::decl attribute.
+
+   directive-name ,[opt] clause-list[opt]
+
+   For directive just remember the tokens in a vector for subsequent
+   parsing.  */
+
+static void
+c_parser_omp_directive_args (c_parser *parser, tree attribute, bool decl_p)
+{
+  unsigned int n = 1;
+  c_token *first = c_parser_peek_token (parser);
+  if (!c_parser_check_balanced_raw_token_sequence (parser, &n)
+      || (c_parser_peek_nth_token_raw (parser, n)->type
+	  != CPP_CLOSE_PAREN))
+    {
+      c_parser_balanced_token_sequence (parser);
+      TREE_VALUE (attribute) = NULL_TREE;
+      return;
+    }
+  if (n == 1)
+    {
+      error_at (first->location, "expected OpenMP directive name");
+      TREE_VALUE (attribute) = NULL_TREE;
+      return;
+    }
+  vec<c_token, va_gc> *v;
+  vec_alloc (v, n - 1);
+  for (--n; n; --n)
+    {
+      c_token *tok = c_parser_peek_token (parser);
+      v->quick_push (*tok);
+      c_parser_consume_token (parser);
+    }
+  tree arg = make_node (C_TOKEN_VEC);
+  C_TOKEN_VEC_TOKENS (arg) = v;
+  if (decl_p)
+    TREE_PUBLIC (arg) = 1;
+  TREE_VALUE (attribute) = tree_cons (NULL_TREE, arg, TREE_VALUE (attribute));
+}
+
+/* Parse arguments of omp::sequence attribute.
+
+   omp::[opt] directive-attr [ , omp::[opt] directive-attr ]...  */
+
+static void
+c_parser_omp_sequence_args (c_parser *parser, tree attribute)
+{
+  do
+    {
+      c_token *token = c_parser_peek_token (parser);
+      if (token->type == CPP_NAME
+	  && strcmp (IDENTIFIER_POINTER (token->value), "omp") == 0
+	  && c_parser_peek_2nd_token (parser)->type == CPP_SCOPE)
+	{
+	  c_parser_consume_token (parser);
+	  c_parser_consume_token (parser);
+	  token = c_parser_peek_token (parser);
+	}
+      bool directive = false;
+      const char *p;
+      if (token->type != CPP_NAME)
+	p = "";
+      else
+	p = IDENTIFIER_POINTER (token->value);
+      if (strcmp (p, "directive") == 0)
+	directive = true;
+      else if (strcmp (p, "sequence") != 0)
+	{
+	  error_at (token->location, "expected %<directive%> or %<sequence%>");
+	  unsigned nesting_depth = 0;
+
+	  while (true)
+	    {
+	      /* Peek at the next token.  */
+	      token = c_parser_peek_token (parser);
+	      /* If we've reached the token we want, consume it and stop.  */
+	      if ((token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA)
+		  && !nesting_depth)
+		break;
+	      /* If we've run out of tokens, stop.  */
+	      if (token->type == CPP_EOF)
+		break;
+	      if (token->type == CPP_PRAGMA_EOL && parser->in_pragma)
+		break;
+	      if (token->type == CPP_OPEN_BRACE
+		  || token->type == CPP_OPEN_PAREN
+		  || token->type == CPP_OPEN_SQUARE)
+		++nesting_depth;
+	      else if (token->type == CPP_CLOSE_BRACE
+		       || token->type == CPP_CLOSE_PAREN
+		       || token->type == CPP_CLOSE_SQUARE)
+		{
+		  if (nesting_depth-- == 0)
+		    break;
+		}
+	      /* Consume this token.  */
+	      c_parser_consume_token (parser);
+	    }
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	  continue;
+	}
+      c_parser_consume_token (parser);
+      matching_parens parens;
+      if (parens.require_open (parser))
+	{
+	  if (directive)
+	    c_parser_omp_directive_args (parser, attribute, false);
+	  else
+	    c_parser_omp_sequence_args (parser, attribute);
+	  parens.skip_until_found_close (parser);
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	}
+      else if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	break;
+      else
+	c_parser_consume_token (parser);
+    }
+  while (1);
+}
+
 /* Parse standard (C2X) attributes (including GNU attributes in the
    gnu:: namespace).
 
@@ -5512,7 +5737,19 @@  c_parser_std_attribute (c_parser *parser
   /* Parse the arguments, if any.  */
   const attribute_spec *as = lookup_attribute_spec (TREE_PURPOSE (attribute));
   if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-    goto out;
+    {
+      if ((flag_openmp || flag_openmp_simd)
+	  && ns
+	  && is_attribute_p ("omp", ns)
+	  && (is_attribute_p ("directive", name)
+	      || is_attribute_p ("sequence", name)
+	      || is_attribute_p ("decl", name)))
+	{
+	  error ("%<omp::%E%> attribute requires argument", name);
+	  return error_mark_node;
+	}
+      goto out;
+    }
   {
     location_t open_loc = c_parser_peek_token (parser)->location;
     matching_parens parens;
@@ -5549,7 +5786,37 @@  c_parser_std_attribute (c_parser *parser
 					  require_string, assume_attr, false);
       }
     else
-      c_parser_balanced_token_sequence (parser);
+      {
+	if ((flag_openmp || flag_openmp_simd)
+	    && ns
+	    && is_attribute_p ("omp", ns))
+	  {
+	    if (is_attribute_p ("directive", name))
+	      {
+		c_parser_omp_directive_args (parser, attribute, false);
+		parens.skip_until_found_close (parser);
+		return attribute;
+	      }
+/*	    else if (is_attribute_p ("decl", name))
+	      {
+		TREE_VALUE (TREE_PURPOSE (attribute))
+		  = get_identifier ("directive");
+		c_parser_omp_directive_args (parser, attribute, true);
+		parens.skip_until_found_close (parser);
+		return attribute;
+	      } */
+	    else if (is_attribute_p ("sequence", name))
+	      {
+		TREE_VALUE (TREE_PURPOSE (attribute))
+		  = get_identifier ("directive");
+		c_parser_omp_sequence_args (parser, attribute);
+		parens.skip_until_found_close (parser);
+		TREE_VALUE (attribute) = nreverse (TREE_VALUE (attribute));
+		return attribute;
+	      }
+	  }
+	c_parser_balanced_token_sequence (parser);
+      }
     parens.require_close (parser);
   }
  out:
@@ -6322,6 +6589,350 @@  add_structured_block_stmt (tree sl)
     add_stmt (build1 (OMP_STRUCTURED_BLOCK, void_type_node, sl));
 }
 
+struct c_omp_attribute_data
+{
+  vec<c_token, va_gc> *tokens;
+  const c_omp_directive *dir;
+  c_omp_directive_kind kind;
+};
+
+/* Handle omp::directive and omp::sequence attributes in ATTRS
+   (if any) at the start of a statement or in attribute-declaration.  */
+
+static bool
+c_parser_handle_statement_omp_attributes (c_parser *parser, tree &attrs,
+					  bool *have_std_attrs)
+{
+  if (!flag_openmp && !flag_openmp_simd)
+    return false;
+
+  auto_vec<c_omp_attribute_data, 16> vd;
+  int cnt = 0;
+  int tokens = 0;
+  bool bad = false;
+  for (tree *pa = &attrs; *pa; )
+    if (is_attribute_namespace_p ("omp", *pa)
+	&& is_attribute_p ("directive", get_attribute_name (*pa)))
+      {
+	cnt++;
+	for (tree a = TREE_VALUE (*pa); a; a = TREE_CHAIN (a))
+	  {
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    vec<c_token, va_gc> *toks = C_TOKEN_VEC_TOKENS (d);
+	    c_token *first = toks->address ();
+	    c_token *last = first + toks->length ();
+	    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;
+	      }
+	    else if (TREE_PUBLIC (d))
+	      {
+		error_at (first->location,
+			  "OpenMP %<omp::decl%> attribute on a statement");
+		bad = true;
+	      }
+	    const char *directive[3] = {};
+	    for (int i = 0; i < 3; i++)
+	      {
+		tree id = NULL_TREE;
+		if (first + i == last)
+		  break;
+		if (first[i].type == CPP_NAME)
+		  id = first[i].value;
+		else if (first[i].type == CPP_KEYWORD)
+		  id = ridpointers[(int) first[i].keyword];
+		else
+		  break;
+		directive[i] = IDENTIFIER_POINTER (id);
+	      }
+	    const c_omp_directive *dir = NULL;
+	    if (directive[0])
+	      dir = c_omp_categorize_directive (directive[0], directive[1],
+						directive[2]);
+	    if (dir == NULL)
+	      {
+		error_at (first->location,
+			  "unknown OpenMP directive name in %qs attribute "
+			  "argument",
+			  TREE_PUBLIC (d) ? "omp::decl" : "omp::directive");
+		continue;
+	      }
+	    c_omp_directive_kind kind = dir->kind;
+	    if (dir->id == PRAGMA_OMP_ORDERED)
+	      {
+		/* ordered is C_OMP_DIR_CONSTRUCT only if it doesn't contain
+		   depend/doacross clause.  */
+		if (directive[1]
+		    && (strcmp (directive[1], "depend") == 0
+			|| strcmp (directive[1], "doacross") == 0))
+		  kind = C_OMP_DIR_STANDALONE;
+		else if (first + 2 < last
+			 && first[1].type == CPP_COMMA
+			 && first[2].type == CPP_NAME
+			 && (strcmp (IDENTIFIER_POINTER (first[2].value),
+				     "depend") == 0
+			     || strcmp (IDENTIFIER_POINTER (first[2].value),
+					"doacross") == 0))
+		  kind = C_OMP_DIR_STANDALONE;
+	      }
+	    else if (dir->id == PRAGMA_OMP_ERROR)
+	      {
+		/* error with at(execution) clause is C_OMP_DIR_STANDALONE.  */
+		int paren_depth = 0;
+		for (int i = 1; first + i < last; i++)
+		  if (first[i].type == CPP_OPEN_PAREN)
+		    paren_depth++;
+		  else if (first[i].type == CPP_CLOSE_PAREN)
+		    paren_depth--;
+		  else if (paren_depth == 0
+			   && first + i + 2 < last
+			   && first[i].type == CPP_NAME
+			   && first[i + 1].type == CPP_OPEN_PAREN
+			   && first[i + 2].type == CPP_NAME
+			   && !strcmp (IDENTIFIER_POINTER (first[i].value),
+				       "at")
+			   && !strcmp (IDENTIFIER_POINTER (first[i
+								 + 2].value),
+				       "execution"))
+		    {
+		      kind = C_OMP_DIR_STANDALONE;
+		      break;
+		    }
+	      }
+	    c_omp_attribute_data v = { toks, dir, kind };
+	    vd.safe_push (v);
+	    if (flag_openmp || dir->simd)
+	      tokens += (last - first) + 1;
+	  }
+	c_omp_attribute_data v = {};
+	vd.safe_push (v);
+	*pa = TREE_CHAIN (*pa);
+      }
+    else
+      pa = &TREE_CHAIN (*pa);
+
+  if (bad)
+    {
+    fail:
+      if (have_std_attrs && attrs == NULL)
+	*have_std_attrs = false;
+      return false;
+    }
+
+  unsigned int i;
+  c_omp_attribute_data *v;
+  c_omp_attribute_data *construct_seen = nullptr;
+  c_omp_attribute_data *standalone_seen = nullptr;
+  c_omp_attribute_data *prev_standalone_seen = nullptr;
+  FOR_EACH_VEC_ELT (vd, i, v)
+    if (v->tokens)
+      {
+	if (v->kind == C_OMP_DIR_CONSTRUCT && !construct_seen)
+	  construct_seen = v;
+	else if (v->kind == C_OMP_DIR_STANDALONE && !standalone_seen)
+	  standalone_seen = v;
+      }
+    else
+      {
+	if (standalone_seen && !prev_standalone_seen)
+	  {
+	    prev_standalone_seen = standalone_seen;
+	    standalone_seen = nullptr;
+	  }
+      }
+
+  if (cnt > 1 && construct_seen)
+    {
+      error_at ((*construct_seen->tokens)[0].location,
+		"OpenMP construct among %<omp::directive%> attributes"
+		" requires all %<omp::directive%> attributes on the"
+		" same statement to be in the same %<omp::sequence%>");
+      goto fail;
+    }
+  if (cnt > 1 && standalone_seen && prev_standalone_seen)
+    {
+      error_at ((*standalone_seen->tokens)[0].location,
+		"multiple OpenMP standalone directives among"
+		" %<omp::directive%> attributes must be all within the"
+		" same %<omp::sequence%>");
+      goto fail;
+    }
+
+  if (prev_standalone_seen)
+    standalone_seen = prev_standalone_seen;
+  if (standalone_seen
+      && !c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      error_at (standalone_seen->tokens->address ()->location,
+		"standalone OpenMP directives in %<omp::directive%> attribute"
+		" can only appear on an empty statement");
+      goto fail;
+    }
+  if (cnt && c_parser_next_token_is (parser, CPP_PRAGMA))
+    {
+      c_token *token = c_parser_peek_token (parser);
+      enum pragma_kind kind = token->pragma_kind;
+      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");
+	  goto fail;
+	}
+    }
+
+  if (!tokens)
+    return false;
+
+  unsigned int tokens_avail = parser->tokens_avail;
+  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
+
+  tokens++;
+  vec<c_token, va_gc> *toks = NULL;
+  vec_safe_reserve (toks, tokens, true);
+  FOR_EACH_VEC_ELT (vd, i, v)
+    {
+      if (!v->tokens)
+	continue;
+      if (!flag_openmp && !v->dir->simd)
+	continue;
+      c_token *first = v->tokens->address ();
+      c_token *last = first + v->tokens->length ();
+      c_token tok = {};
+      tok.type = CPP_PRAGMA;
+      tok.keyword = RID_MAX;
+      tok.pragma_kind = pragma_kind (v->dir->id);
+      tok.location = first->location;
+      toks->quick_push (tok);
+      while (++first < last)
+	toks->quick_push (*first);
+      tok = {};
+      tok.type = CPP_PRAGMA_EOL;
+      tok.keyword = RID_MAX;
+      tok.location = last[-1].location;
+      toks->quick_push (tok);
+    }
+
+  c_token tok = {};
+  tok.type = CPP_EOF;
+  tok.keyword = RID_MAX;
+  tok.location = toks->last ().location;
+  tok.flags = tokens_avail;
+  toks->quick_push (tok);
+
+  parser->tokens = toks->address ();
+  parser->tokens_avail = tokens;
+  parser->in_omp_attribute_pragma = toks;
+  return true;
+}
+
+/* Handle omp::directive and omp::sequence attributes in ATTRS
+   (if any) at the start or after declaration-id of a declaration.  */
+
+static void
+c_parser_handle_directive_omp_attributes (tree &attrs,
+					  vec<c_token> *&pragma_clauses,
+					  vec<c_token> *attr_clauses)
+{
+  if (!flag_openmp && !flag_openmp_simd)
+    return;
+
+  for (tree *pa = &attrs; *pa; )
+    if (is_attribute_namespace_p ("omp", *pa)
+	&& is_attribute_p ("directive", get_attribute_name (*pa)))
+      {
+	int cnt = 0;
+	for (tree *pa2 = &TREE_VALUE (*pa); *pa2; )
+	  {
+	    tree a = *pa2;
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    vec<c_token, va_gc> *toks = C_TOKEN_VEC_TOKENS (d);
+	    c_token *first = toks->address ();
+	    c_token *last = first + toks->length ();
+	    const char *directive[3] = {};
+	    for (int i = 0; i < 3; i++)
+	      {
+		tree id = NULL_TREE;
+		if (first + i == last)
+		  break;
+		if (first[i].type == CPP_NAME)
+		  id = first[i].value;
+		else if (first[i].type == CPP_KEYWORD)
+		  id = ridpointers[(int) first[i].keyword];
+		else
+		  break;
+		directive[i] = IDENTIFIER_POINTER (id);
+	      }
+	    const c_omp_directive *dir = NULL;
+	    if (directive[0])
+	      dir = c_omp_categorize_directive (directive[0], directive[1],
+						directive[2]);
+	    if (dir == NULL)
+	      {
+		error_at (first->location,
+			  "unknown OpenMP directive name in "
+			  "%qs attribute argument",
+			  TREE_PUBLIC (d) ? "omp::decl" : "omp::directive");
+		*pa2 = TREE_CHAIN (a);
+	      }
+	    else if (dir->id == PRAGMA_OMP_DECLARE
+		     && (strcmp (directive[1], "simd") == 0
+			 || strcmp (directive[1], "variant") == 0))
+	      {
+		if (pragma_clauses)
+		  {
+		    error_at (first->location,
+			      "mixing OpenMP directives with attribute and "
+			      "pragma syntax on the same declaration");
+		    for (pa = &attrs; *pa; )
+		      if (is_attribute_namespace_p ("omp", *pa)
+			  && is_attribute_p ("directive",
+					     get_attribute_name (*pa)))
+			*pa = TREE_CHAIN (*pa);
+		      else
+			pa = &TREE_CHAIN (*pa);
+		    return;
+		  }
+		++cnt;
+		attr_clauses->reserve (attr_clauses->length ()
+				       + toks->length () + 2);
+		for (++first; first < last; ++first)
+		  attr_clauses->quick_push (*first);
+		c_token tok = {};
+		tok.type = CPP_PRAGMA_EOL;
+		tok.keyword = RID_MAX;
+		tok.location = last[-1].location;
+		attr_clauses->quick_push (tok);
+		*pa2 = TREE_CHAIN (a);
+	      }
+	    else
+	      pa2 = &TREE_CHAIN (a);
+	  }
+	if (cnt && TREE_VALUE (*pa) == NULL_TREE)
+	  *pa = TREE_CHAIN (*pa);
+	else
+	  pa = &TREE_CHAIN (*pa);
+      }
+    else
+      pa = &TREE_CHAIN (*pa);
+  if (attr_clauses->length ())
+    {
+      c_token tok = {};
+      tok.type = CPP_EOF;
+      tok.keyword = RID_MAX;
+      tok.location = attr_clauses->last ().location;
+      attr_clauses->quick_push (tok);
+      attr_clauses->quick_push (tok);
+      pragma_clauses = attr_clauses;
+    }
+}
+
 /* Parse a compound statement except for the opening brace.  This is
    used for parsing both compound statements and statement expressions
    (which follow different paths to handling the opening).  */
@@ -6495,7 +7106,10 @@  c_parser_compound_statement_nostart (c_p
 	}
       else if (c_parser_next_tokens_start_declaration (parser)
 	       || (have_std_attrs
-		   && c_parser_next_token_is (parser, CPP_SEMICOLON)))
+		   && !c_parser_handle_statement_omp_attributes
+				(parser, std_attrs, &have_std_attrs)
+		   && c_parser_next_token_is (parser, CPP_SEMICOLON)
+		   && (have_std_attrs = true)))
 	{
 	  if (last_label)
 	    pedwarn_c11 (c_parser_peek_token (parser)->location, OPT_Wpedantic,
@@ -6569,8 +7183,10 @@  c_parser_compound_statement_nostart (c_p
 	}
       else if (c_parser_next_token_is (parser, CPP_PRAGMA))
 	{
-	  if (have_std_attrs)
+	  if (have_std_attrs && !parser->in_omp_attribute_pragma)
 	    c_parser_error (parser, "expected declaration or statement");
+	  else if (std_attrs)
+	    c_warn_unused_attributes (std_attrs);
 	  /* External pragmas, and some omp pragmas, are not associated
 	     with regular c code, and so are not to be considered statements
 	     syntactically.  This ensures that the user doesn't put them
@@ -6588,7 +7204,7 @@  c_parser_compound_statement_nostart (c_p
 		check_omp_intervening_code (parser);
 	    }
 	  if (omp_for_parse_state)
-	      omp_for_parse_state->want_nested_loop = want_nested_loop;
+	    omp_for_parse_state->want_nested_loop = want_nested_loop;
 	}
       else if (c_parser_next_token_is (parser, CPP_EOF))
 	{
@@ -6666,13 +7282,10 @@  c_parser_compound_statement_nostart (c_p
 static void
 c_parser_all_labels (c_parser *parser)
 {
+  bool have_std_attrs;
   tree std_attrs = NULL;
-  if (c_parser_nth_token_starts_std_attributes (parser, 1))
-    {
-      std_attrs = c_parser_std_attribute_specifier_sequence (parser);
-      if (c_parser_next_token_is (parser, CPP_SEMICOLON))
-	c_parser_error (parser, "expected statement");
-    }
+  if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1)))
+    std_attrs = c_parser_std_attribute_specifier_sequence (parser);
   while (c_parser_next_token_is_keyword (parser, RID_CASE)
 	 || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
 	 || (c_parser_next_token_is (parser, CPP_NAME)
@@ -6680,15 +7293,20 @@  c_parser_all_labels (c_parser *parser)
     {
       c_parser_label (parser, std_attrs);
       std_attrs = NULL;
-      if (c_parser_nth_token_starts_std_attributes (parser, 1))
-	{
-	  std_attrs = c_parser_std_attribute_specifier_sequence (parser);
-	  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
-	    c_parser_error (parser, "expected statement");
-	}
+      if ((have_std_attrs = c_parser_nth_token_starts_std_attributes (parser,
+								      1)))
+	std_attrs = c_parser_std_attribute_specifier_sequence (parser);
     }
-   if (std_attrs)
-     c_warn_unused_attributes (std_attrs);
+  if (std_attrs
+      && (!c_parser_handle_statement_omp_attributes (parser, std_attrs, &have_std_attrs)
+	  || std_attrs))
+    {
+      if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON))
+	c_parser_error (parser, "expected statement");
+      c_warn_unused_attributes (std_attrs);
+    }
+  else if (have_std_attrs && c_parser_next_token_is (parser, CPP_SEMICOLON))
+    c_parser_error (parser, "expected statement");
 }
 
 /* Parse a label (C90 6.6.1, C99 6.8.1, C11 6.8.1).
@@ -6935,6 +7553,7 @@  c_parser_statement (c_parser *parser, bo
   c_parser_all_labels (parser);
   if (loc_after_labels)
     *loc_after_labels = c_parser_peek_token (parser)->location;
+  parser->omp_attrs_forbidden_p = false;
   c_parser_statement_after_labels (parser, if_p, NULL);
 }
 
@@ -14206,6 +14825,7 @@  c_parser_omp_variable_list (c_parser *pa
   bool array_section_p;
   auto_vec<c_token> tokens;
   unsigned int tokens_avail = 0;
+  c_token *saved_tokens = NULL;
   bool first = true;
 
   while (1)
@@ -14285,8 +14905,8 @@  c_parser_omp_variable_list (c_parser *pa
 	  tokens.safe_push (eof_token);
 	  tokens.safe_push (eof_token);
 
+	  saved_tokens = parser->tokens;
 	  tokens_avail = parser->tokens_avail;
-	  gcc_assert (parser->tokens == &parser->tokens_buf[0]);
 	  parser->tokens = tokens.address ();
 	  parser->tokens_avail = tokens.length ();
 	}
@@ -14503,7 +15123,7 @@  c_parser_omp_variable_list (c_parser *pa
 
       if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
 	{
-	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
       if (c_parser_next_token_is_not (parser, CPP_COMMA))
@@ -18677,6 +19297,7 @@  static tree
 c_parser_omp_structured_block (c_parser *parser, bool *if_p)
 {
   tree stmt = push_stmt_list ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   return pop_stmt_list (stmt);
 }
@@ -20786,6 +21407,79 @@  c_parser_omp_flush (c_parser *parser)
   c_finish_omp_flush (loc, mo);
 }
 
+/* Return true if next tokens contain a standard attribute that contains
+   omp::directive (DIRECTIVE).  */
+
+static bool
+c_parser_omp_section_scan (c_parser *parser, const char *directive,
+			   bool tentative)
+{
+  if (!c_parser_nth_token_starts_std_attributes (parser, 1))
+    return false;
+  unsigned int n = 3;
+  if (!c_parser_check_balanced_raw_token_sequence (parser, &n))
+    return false;
+  c_token *token = c_parser_peek_nth_token_raw (parser, n);
+  if (token->type != CPP_CLOSE_SQUARE)
+    return false;
+  token = c_parser_peek_nth_token_raw (parser, n + 1);
+  if (token->type != CPP_CLOSE_SQUARE)
+    return false;
+  if (n < 9)
+    return false;
+  if (c_parser_peek_nth_token_raw (parser, 3)->type == CPP_NAME
+      && c_parser_peek_nth_token_raw (parser, 4)->type == CPP_OPEN_PAREN
+      && c_parser_peek_nth_token_raw (parser, 5)->type == CPP_NAME)
+    {
+      tree first = c_parser_peek_nth_token_raw (parser, 3)->value;
+      tree second = c_parser_peek_nth_token_raw (parser, 5)->value;
+      if (strcmp (IDENTIFIER_POINTER (first), "directive")
+	  && strcmp (IDENTIFIER_POINTER (first), "__directive__"))
+	return false;
+      if (strcmp (IDENTIFIER_POINTER (second), directive))
+	return false;
+    }
+  if (tentative)
+    return true;
+  location_t first_loc = c_parser_peek_token (parser)->location;
+  location_t last_loc = c_parser_peek_nth_token_raw (parser, n + 1)->location;
+  location_t middle_loc = UNKNOWN_LOCATION;
+  tree std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+  bool seen = false;
+  int cnt = 0;
+  for (tree attr = std_attrs; attr; attr = TREE_CHAIN (attr))
+    if (is_attribute_namespace_p ("omp", attr)
+	&& is_attribute_p ("directive", get_attribute_name (attr)))
+      {
+	for (tree a = TREE_VALUE (attr); a; a = TREE_CHAIN (a))
+	  {
+	    tree d = TREE_VALUE (a);
+	    gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+	    c_token *first = C_TOKEN_VEC_TOKENS (d)->address ();
+	    cnt++;
+	    if (first->type == CPP_NAME
+		&& strcmp (IDENTIFIER_POINTER (first->value),
+			   directive) == 0)
+	      {
+		seen = true;
+		if (middle_loc == UNKNOWN_LOCATION)
+		  middle_loc = first->location;
+	      }
+	  }
+      }
+  if (!seen)
+    return false;
+  if (cnt != 1 || TREE_CHAIN (std_attrs))
+    {
+      error_at (make_location (first_loc, last_loc, middle_loc),
+		"%<[[omp::directive(%s)]]%> must be the only specified "
+		"attribute on a statement", directive);
+      return false;
+    }
+  c_parser_handle_statement_omp_attributes (parser, std_attrs, NULL);
+  return true;
+}
+
 /* Parse an OpenMP structured block sequence.  KIND is the corresponding
    separating directive.  */
 
@@ -20805,6 +21499,13 @@  c_parser_omp_structured_block_sequence (
       if (kind != PRAGMA_NONE
 	  && c_parser_peek_token (parser)->pragma_kind == kind)
 	break;
+
+      if (kind != PRAGMA_NONE
+	  && c_parser_omp_section_scan (parser,
+					kind == PRAGMA_OMP_SCAN
+					? "scan" : "section", false))
+	break;
+
       c_parser_statement (parser, NULL);
     }
   while (1);
@@ -21794,7 +22495,8 @@  c_parser_omp_sections_scope (location_t
 
   stmt = push_stmt_list ();
 
-  if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_SECTION)
+  if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_SECTION
+      && !c_parser_omp_section_scan (parser, "section", true))
     {
       substmt = c_parser_omp_structured_block_sequence (parser,
 							PRAGMA_OMP_SECTION);
@@ -21811,6 +22513,7 @@  c_parser_omp_sections_scope (location_t
 	break;
 
       loc = c_parser_peek_token (parser)->location;
+      c_parser_omp_section_scan (parser, "section", false);
       if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_SECTION)
 	{
 	  c_parser_consume_pragma (parser);
@@ -22072,6 +22775,7 @@  c_parser_omp_parallel (location_t loc, c
     }
 
   block = c_begin_omp_parallel ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   stmt = c_finish_omp_parallel (loc, clauses, block);
 
@@ -22167,6 +22871,7 @@  c_parser_omp_task (location_t loc, c_par
 				      "#pragma omp task");
 
   block = c_begin_omp_task ();
+  parser->omp_attrs_forbidden_p = true;
   c_parser_statement (parser, if_p);
   return c_finish_omp_task (loc, clauses, block);
 }
@@ -23205,6 +23910,13 @@  c_parser_omp_declare_simd (c_parser *par
 	     IDENTIFIER_POINTER (kind));
       break;
     case pragma_compound:
+      bool have_std_attrs;
+      tree std_attrs;
+      have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1);
+      if (have_std_attrs)
+	std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+      else
+	std_attrs = NULL_TREE;
       if (c_parser_next_token_is (parser, CPP_KEYWORD)
 	  && c_parser_peek_token (parser)->keyword == RID_EXTENSION)
 	{
@@ -23213,10 +23925,12 @@  c_parser_omp_declare_simd (c_parser *par
 	    c_parser_consume_token (parser);
 	  while (c_parser_next_token_is (parser, CPP_KEYWORD)
 		 && c_parser_peek_token (parser)->keyword == RID_EXTENSION);
-	  if (c_parser_next_tokens_start_declaration (parser))
+	  if (c_parser_next_tokens_start_declaration (parser)
+	      || c_parser_nth_token_starts_std_attributes (parser, 1))
 	    {
 	      c_parser_declaration_or_fndef (parser, true, true, true, true,
-					     true, NULL, &clauses);
+					     true, NULL, &clauses,
+					     have_std_attrs, std_attrs);
 	      restore_extension_diagnostics (ext);
 	      break;
 	    }
@@ -23225,7 +23939,8 @@  c_parser_omp_declare_simd (c_parser *par
       else if (c_parser_next_tokens_start_declaration (parser))
 	{
 	  c_parser_declaration_or_fndef (parser, true, true, true, true, true,
-					 NULL, &clauses);
+					 NULL, &clauses, have_std_attrs,
+					 std_attrs);
 	  break;
 	}
       error ("%<#pragma omp declare %s%> must be followed by "
@@ -23753,8 +24468,8 @@  c_finish_omp_declare_simd (c_parser *par
   while (parser->tokens_avail > 3)
     {
       c_token *token = c_parser_peek_token (parser);
-      gcc_assert (token->type == CPP_NAME
-		  && strcmp (IDENTIFIER_POINTER (token->value), kind) == 0);
+      gcc_assert (token->type == CPP_NAME);
+      kind = IDENTIFIER_POINTER (token->value);
       c_parser_consume_token (parser);
       parser->in_pragma = true;
 
@@ -23820,8 +24535,9 @@  c_parser_omp_declare_target (c_parser *p
     }
   else
     {
+      bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
       c_parser_skip_to_pragma_eol (parser);
-      c_omp_declare_target_attr attr = { -1 };
+      c_omp_declare_target_attr attr = { attr_syntax, -1 };
       vec_safe_push (current_omp_declare_target_attribute, attr);
       return;
     }
@@ -23928,6 +24644,7 @@  c_parser_omp_begin (c_parser *parser)
       if (strcmp (p, "target") == 0)
 	{
 	  c_parser_consume_token (parser);
+	  bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
 	  tree clauses
 	    = c_parser_omp_all_clauses (parser,
 					OMP_BEGIN_DECLARE_TARGET_CLAUSE_MASK,
@@ -23936,7 +24653,7 @@  c_parser_omp_begin (c_parser *parser)
 	  for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
 	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE_TYPE)
 	      device_type |= OMP_CLAUSE_DEVICE_TYPE_KIND (c);
-	  c_omp_declare_target_attr attr = { device_type };
+	  c_omp_declare_target_attr attr = { attr_syntax, device_type };
 	  vec_safe_push (current_omp_declare_target_attribute, attr);
 	}
       else
@@ -23948,8 +24665,10 @@  c_parser_omp_begin (c_parser *parser)
   else if (strcmp (p, "assumes") == 0)
     {
       c_parser_consume_token (parser);
+      bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
       c_parser_omp_assumption_clauses (parser, false);
-      current_omp_begin_assumes++;
+      struct c_omp_begin_assumes_data a = { attr_syntax };
+      vec_safe_push (current_omp_begin_assumes, a);
     }
   else
     {
@@ -23985,23 +24704,61 @@  c_parser_omp_end (c_parser *parser)
 	  c_parser_skip_to_pragma_eol (parser);
 	  return;
 	}
+      bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
       c_parser_skip_to_pragma_eol (parser);
       if (!vec_safe_length (current_omp_declare_target_attribute))
 	error_at (loc, "%<#pragma omp end declare target%> without "
 		       "corresponding %<#pragma omp declare target%> or "
 		       "%<#pragma omp begin declare target%>");
       else
-	current_omp_declare_target_attribute->pop ();
+	{
+	  c_omp_declare_target_attr
+	    a = current_omp_declare_target_attribute->pop ();
+	  if (a.attr_syntax != attr_syntax)
+	    {
+	      if (a.attr_syntax)
+		error_at (loc,
+			  "%qs in attribute syntax terminated "
+			  "with %qs in pragma syntax",
+			  a.device_type >= 0 ? "begin declare target"
+					     : "declare target",
+			  "end declare target");
+	      else
+		error_at (loc,
+			  "%qs in pragma syntax terminated "
+			  "with %qs in attribute syntax",
+			  a.device_type >= 0 ? "begin declare target"
+					     : "declare target",
+			  "end declare target");
+	    }
+	}
     }
   else if (strcmp (p, "assumes") == 0)
     {
       c_parser_consume_token (parser);
+      bool attr_syntax = parser->in_omp_attribute_pragma != NULL;
       c_parser_skip_to_pragma_eol (parser);
-      if (!current_omp_begin_assumes)
+      if (!vec_safe_length (current_omp_begin_assumes))
 	error_at (loc, "%qs without corresponding %qs",
 		  "#pragma omp end assumes", "#pragma omp begin assumes");
       else
-	current_omp_begin_assumes--;
+	{
+	  c_omp_begin_assumes_data
+	    a = current_omp_begin_assumes->pop ();
+	  if (a.attr_syntax != attr_syntax)
+	    {
+	      if (a.attr_syntax)
+		error_at (loc,
+			  "%qs in attribute syntax terminated "
+			  "with %qs in pragma syntax",
+			  "begin assumes", "end assumes");
+	      else
+		error_at (loc,
+			  "%qs in pragma syntax terminated "
+			  "with %qs in attribute syntax",
+			  "begin assumes", "end assumes");
+	    }
+	}
     }
   else
     {
@@ -24022,6 +24779,7 @@  static void
 c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
 {
   unsigned int tokens_avail = 0, i;
+  c_token *saved_tokens = NULL;
   vec<tree> types = vNULL;
   vec<c_token> clauses = vNULL;
   enum tree_code reduc_code = ERROR_MARK;
@@ -24192,8 +24950,8 @@  c_parser_omp_declare_reduction (c_parser
   int errs = errorcount;
   FOR_EACH_VEC_ELT (types, i, type)
     {
+      saved_tokens = parser->tokens;
       tokens_avail = parser->tokens_avail;
-      gcc_assert (parser->tokens == &parser->tokens_buf[0]);
       if (!clauses.is_empty ())
 	{
 	  parser->tokens = clauses.address ();
@@ -24354,7 +25112,7 @@  c_parser_omp_declare_reduction (c_parser
 
       if (!clauses.is_empty ())
 	{
-	  parser->tokens = &parser->tokens_buf[0];
+	  parser->tokens = saved_tokens;
 	  parser->tokens_avail = tokens_avail;
 	}
       if (bad)
--- gcc/c/c-decl.cc.jj	2023-11-02 07:49:14.395902890 +0100
+++ gcc/c/c-decl.cc	2023-11-02 17:12:50.153151004 +0100
@@ -61,6 +61,7 @@  along with GCC; see the file COPYING3.
 #include "context.h"  /* For 'g'.  */
 #include "omp-general.h"
 #include "omp-offload.h"  /* For offload_vars.  */
+#include "c-parser.h"
 
 #include "tree-pretty-print.h"
 
@@ -157,9 +158,10 @@  static bool undef_nested_function;
    the attribute lists.  */
 vec<c_omp_declare_target_attr, va_gc> *current_omp_declare_target_attribute;
 
-/* If non-zero, we are inside of
-   #pragma omp begin assumes ... #pragma omp end assumes region.  */
-int current_omp_begin_assumes;
+/* Vector of
+   #pragma omp begin assumes ... #pragma omp end assumes regions
+   we are in.  */
+vec<c_omp_begin_assumes_data, va_gc> *current_omp_begin_assumes;
 
 /* Each c_binding structure describes one binding of an identifier to
    a decl.  All the decls in a scope - irrespective of namespace - are
@@ -322,18 +324,47 @@  i_label_binding (tree node)
 
 #define I_LABEL_DECL(node) \
  (I_LABEL_BINDING(node) ? I_LABEL_BINDING(node)->decl : 0)
+
+/* Used by C_TOKEN_VEC tree.  */
+struct GTY (()) c_tree_token_vec {
+  struct tree_base base;
+  vec<c_token, va_gc> *tokens;
+};
+
+STATIC_ASSERT (sizeof (c_tree_token_vec) == sizeof (c_tree_token_vec_struct));
+STATIC_ASSERT (offsetof (c_tree_token_vec, tokens)
+	       == offsetof (c_tree_token_vec_struct, tokens));
 
 /* The resulting tree type.  */
 
-union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE + 2 * (TREE_CODE (&%h.generic) == C_TOKEN_VEC)"),
        chain_next ("(union lang_tree_node *) c_tree_chain_next (&%h.generic)"))) lang_tree_node
  {
   union tree_node GTY ((tag ("0"),
 			desc ("tree_node_structure (&%h)")))
     generic;
   struct lang_identifier GTY ((tag ("1"))) identifier;
+  struct c_tree_token_vec GTY ((tag ("2"))) c_token_vec;
 };
 
+/* Langhook for tree_size.  */
+size_t
+c_tree_size (enum tree_code code)
+{
+  gcc_checking_assert (code >= NUM_TREE_CODES);
+  switch (code)
+    {
+    case C_TOKEN_VEC: return sizeof (c_tree_token_vec);
+    default:
+      switch (TREE_CODE_CLASS (code))
+	{
+	case tcc_declaration: return sizeof (tree_decl_non_common);
+	case tcc_type: return sizeof (tree_type_non_common);
+	default: gcc_unreachable ();
+	}
+    }
+}
+
 /* Track bindings and other things that matter for goto warnings.  For
    efficiency, we do not gather all the decls at the point of
    definition.  Instead, we point into the bindings structure.  As
@@ -5335,6 +5366,49 @@  c_decl_attributes (tree *node, tree attr
 	}
     }
 
+  if (flag_openmp || flag_openmp_simd)
+    {
+      bool diagnosed = false;
+      for (tree *pa = &attributes; *pa; )
+	{
+	  if (is_attribute_namespace_p ("omp", *pa))
+	    {
+	      tree name = get_attribute_name (*pa);
+	      if (is_attribute_p ("directive", name)
+		  || is_attribute_p ("sequence", name)
+		  || is_attribute_p ("decl", name))
+		{
+		  const char *p = NULL;
+		  if (TREE_VALUE (*pa) == NULL_TREE)
+		    p = IDENTIFIER_POINTER (name);
+		  for (tree a = TREE_VALUE (*pa); a; a = TREE_CHAIN (a))
+		    {
+		      tree d = TREE_VALUE (a);
+		      gcc_assert (TREE_CODE (d) == C_TOKEN_VEC);
+/*		      if (TREE_PUBLIC (d)
+			  && (VAR_P (*decl)
+			      || TREE_CODE (*decl) == FUNCTION_DECL)
+			  && c_maybe_parse_omp_decl (*decl, d))
+			continue; */
+		      p = TREE_PUBLIC (d) ? "decl" : "directive";
+		    }
+		  if (p && !diagnosed)
+		    {
+		      error ("%<omp::%s%> not allowed to be specified in "
+			     "this context", p);
+		      diagnosed = true;
+		    }
+		  if (p)
+		    {
+		      *pa = TREE_CHAIN (*pa);
+		      continue;
+		    }
+		}
+	    }
+	  pa = &TREE_CHAIN (*pa);
+	}
+    }
+
   /* Look up the current declaration with all the attributes merged
      so far so that attributes on the current declaration that's
      about to be pushed that conflict with the former can be detected,
--- gcc/c/c-objc-common.h.jj	2023-11-02 07:48:27.598554687 +0100
+++ gcc/c/c-objc-common.h	2023-11-02 12:21:36.557631540 +0100
@@ -26,6 +26,8 @@  along with GCC; see the file COPYING3.
 
 #undef LANG_HOOKS_IDENTIFIER_SIZE
 #define LANG_HOOKS_IDENTIFIER_SIZE C_SIZEOF_STRUCT_LANG_IDENTIFIER
+#undef LANG_HOOKS_TREE_SIZE
+#define LANG_HOOKS_TREE_SIZE c_tree_size
 #undef LANG_HOOKS_FINISH
 #define LANG_HOOKS_FINISH c_common_finish
 #undef LANG_HOOKS_OPTION_LANG_MASK
--- gcc/cp/parser.h.jj	2023-11-02 07:48:27.805551804 +0100
+++ gcc/cp/parser.h	2023-11-02 12:21:36.543631735 +0100
@@ -408,7 +408,8 @@  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.  */
+  /* TRUE if omp::directive, omp::decl or omp::sequence attributes may not
+     appear.  */
   bool omp_attrs_forbidden_p;
 
   /* Tracks the function's template parameter list when declaring a function
--- gcc/cp/parser.cc.jj	2023-11-02 07:49:16.068879589 +0100
+++ gcc/cp/parser.cc	2023-11-02 12:21:36.550631638 +0100
@@ -43886,7 +43886,8 @@  cp_parser_omp_section_scan (cp_parser *p
       {
 	tree first = cp_lexer_peek_nth_token (parser->lexer, i)->u.value;
 	tree second = cp_lexer_peek_nth_token (parser->lexer, i + 2)->u.value;
-	if (strcmp (IDENTIFIER_POINTER (first), "directive"))
+	if (strcmp (IDENTIFIER_POINTER (first), "directive")
+	    && strcmp (IDENTIFIER_POINTER (first), "__directive__"))
 	  continue;
 	if (strcmp (IDENTIFIER_POINTER (second), directive) == 0)
 	  break;
--- gcc/objc/objc-act.h.jj	2023-11-02 07:48:27.857551080 +0100
+++ gcc/objc/objc-act.h	2023-11-02 12:21:36.558631526 +0100
@@ -738,8 +738,6 @@  struct objc_try_context
 
 extern tree objc_create_temporary_var (tree, const char *);
 
-size_t objc_common_tree_size (enum tree_code code);
-
 
 #define objc_is_object_id(TYPE) (OBJC_TYPE_NAME (TYPE) == objc_object_id)
 #define objc_is_class_id(TYPE) (OBJC_TYPE_NAME (TYPE) == objc_class_id)
--- gcc/objc/objc-act.cc.jj	2023-11-02 07:48:27.821551582 +0100
+++ gcc/objc/objc-act.cc	2023-11-02 12:21:36.559631512 +0100
@@ -10340,24 +10340,5 @@  objc_common_init_ts (void)
   MARK_TS_TYPED (PROPERTY_REF);
 }
 
-size_t
-objc_common_tree_size (enum tree_code code)
-{
-  switch (code)
-    {
-    case CLASS_METHOD_DECL:
-    case INSTANCE_METHOD_DECL:
-    case KEYWORD_DECL:
-    case PROPERTY_DECL:			return sizeof (tree_decl_non_common);
-    case CLASS_INTERFACE_TYPE:
-    case CLASS_IMPLEMENTATION_TYPE:
-    case CATEGORY_INTERFACE_TYPE:
-    case CATEGORY_IMPLEMENTATION_TYPE:
-    case PROTOCOL_INTERFACE_TYPE:	return sizeof (tree_type_non_common);
-    default:
-      gcc_unreachable ();
-    }
-}
-
 
 #include "gt-objc-objc-act.h"
--- gcc/objc/objc-lang.cc.jj	2023-11-02 07:48:27.897550523 +0100
+++ gcc/objc/objc-lang.cc	2023-11-02 12:21:36.558631526 +0100
@@ -44,8 +44,6 @@  enum c_language_kind c_language = clk_ob
 #define LANG_HOOKS_GIMPLIFY_EXPR objc_gimplify_expr
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS objc_common_init_ts
-#undef LANG_HOOKS_TREE_SIZE
-#define LANG_HOOKS_TREE_SIZE objc_common_tree_size
 #undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 #define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language
 
--- gcc/testsuite/gcc.dg/gomp/attrs-1.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-1.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-1.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-2.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-2.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-2.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-3.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-3.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,39 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+int i;
+int t1, t2, t3, t4, t5, t6, t7;
+
+void
+foo ()
+{
+  [[omp::directive]];		/* { dg-error "'omp::directive' attribute requires argument" } */
+  [[omp::directive ()]];	/* { dg-error "expected OpenMP directive name" } */
+  [[omp::directive (nonexistent foobar)]];	/* { dg-error "unknown OpenMP directive name in 'omp::directive' attribute argument" } */
+  [[omp::sequence]];		/* { dg-error "'omp::sequence' attribute requires argument" } */
+  [[omp::sequence()]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(foobar())]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(omp::foobar())]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(directive(taskwait), foobar())]];		/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(omp::directive(taskwait), omp::foobar())]];	/* { dg-error "expected 'directive' or 'sequence'" } */
+  [[omp::sequence(directive(taskwait) foobar())]];		/* { dg-error "expected '\\\)' before 'foobar'" } */
+  [[omp::sequence(directive)]];		/* { dg-error "expected '\\\(' before '\\\)' token" } */
+  [[omp::sequence(omp::sequence)]];	/* { dg-error "expected '\\\(' before '\\\)' token" } */
+  [[omp::directive (parallel), omp::directive (single)]]	/* { dg-error "OpenMP construct among 'omp::directive' attributes requires all 'omp::directive' attributes on the same statement to be in the same 'omp::sequence'" } */
+    ;
+  [[omp::directive (parallel)]]	/* { dg-error "OpenMP construct among 'omp::directive' attributes requires all 'omp::directive' attributes on the same statement to be in the same 'omp::sequence'" } */
+  [[omp::directive (single)]]
+    ;
+  [[omp::directive (taskwait), omp::directive (taskyield)]]	/* { dg-error "multiple OpenMP standalone directives among 'omp::directive' attributes must be all within the same 'omp::sequence'" } */
+    ;
+  [[omp::directive (taskwait)]]
+  [[omp::directive (taskyield)]]	/* { dg-error "multiple OpenMP standalone directives among 'omp::directive' attributes must be all within the same 'omp::sequence'" } */
+    ;
+  [[omp::directive (flush)]]	/* { dg-error "standalone OpenMP directives in 'omp::directive' attribute can only appear on an empty statement" } */
+    i++;
+  int [[omp::directive (threadprivate (t2))]] b;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  int *[[omp::directive (threadprivate (t3))]] c;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  typedef int T [[omp::directive (threadprivate (t5))]];	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  int e[10] [[omp::directive (threadprivate (t6))]];		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  struct [[omp::directive (threadprivate (t7))]] S {};		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-4.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-4.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,62 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+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/gcc.dg/gomp/attrs-5.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-5.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-5.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-6.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-6.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,104 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+void
+foo ()
+{
+  int a[10] = {};
+  #pragma omp parallel sections
+  {
+    #pragma omp section
+    a[0]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    #pragma omp section
+    { a[3]++; }
+  }
+  [[omp::directive (parallel sections)]]
+  {
+    #pragma omp section
+    a[0]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    #pragma omp section
+    { a[3]++; }
+  }
+  #pragma omp parallel sections
+  {
+    #pragma omp section
+    a[0]++;
+    a[4]++;
+    l1: a[5]++;
+    if (a[5] == 42) goto l1;
+    [[omp::directive (section)]] {
+    a[1]++;
+    a[6]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    a[7]++;
+    #pragma omp section
+    { a[3]++; }
+    a[8]++;
+  }
+  [[omp::directive (parallel sections)]]
+  {
+    #pragma omp section
+    a[0]++;
+    a[4]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    a[5]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    l2: a[6]++;
+    if (a[6] == 42)
+      goto l2;
+    a[7]++;
+    #pragma omp section
+    a[8]++;
+    { a[3]++; }
+  }
+}
+
+int
+bar (int a, int *c, int *d, int *e, int *f)
+{
+  int i;
+  #pragma omp simd reduction (inscan, +: a)
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan, exclusive (a))]]
+      a += c[i];
+    }
+  [[omp::directive (simd reduction (inscan, +: a))]]
+  for (i = 0; i < 64; i++)
+    {
+      a += c[i];
+      #pragma omp scan inclusive (a)
+      d[i] = a;
+    }
+  #pragma omp simd reduction (inscan, +: a)
+  for (i = 0; i < 64; i++)
+    {
+      { int t = a;
+	d[i] = t; }
+      [[omp::directive (scan, exclusive (a))]]
+      { int u = c[i];
+	a += u; }
+    }
+  [[omp::directive (simd reduction (inscan, +: a))]]
+  for (i = 0; i < 64; i++)
+    {
+      { int t = c[i];
+	a += t; }
+      #pragma omp scan inclusive (a)
+      { int u = a;
+	d[i] = u; }
+    }
+  return a;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-7.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-7.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,62 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+void
+foo ()
+{
+
+  [[omp::directive (parallel sections)]]
+  {
+    [[omp::directive (parallel)]];
+    [[omp::sequence (directive (section), directive (flush))]];		/* { dg-error "must be the only specified attribute on a statement" } */
+									/* { dg-error "expected '#pragma omp section' or '\\\}'" "" { target *-*-* } .-2 } */
+    [[omp::sequence (directive (flush), omp::directive (section))]];	/* { dg-error "must be the only specified attribute on a statement" } */
+    [[gnu::cold, omp::directive (section)]];				/* { dg-error "must be the only specified attribute on a statement" } */
+    [[omp::directive (section)]] [[gnu::cold]];				/* { dg-error "must be the only specified attribute on a statement" } */
+    [[omp::directive (section foo)]];					/* { dg-error "expected end of line before 'foo'" } */
+  }
+}
+
+int
+bar (int a, int *c, int *d, int *e, int *f)
+{
+  int i;
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::sequence (omp::directive (parallel), omp::directive (scan, exclusive (a)))]]	/* { dg-error "must be the only specified attribute on a statement" } */
+      a += c[i];
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      a += c[i];
+      [[omp::sequence (directive (scan inclusive (a)), directive (critical))]]		/* { dg-error "must be the only specified attribute on a statement" } */
+
+      d[i] = a;
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[gnu::cold]] [[omp::directive (scan, exclusive (a))]]
+											/* { dg-error "#pragma omp scan" "" { target *-*-* } .-1 } */
+      a += c[i];									/* { dg-warning "'cold' attribute ignored" } */
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan, exclusive (a)), gnu::cold]]				/* { dg-error "must be the only specified attribute on a statement" } */
+      a += c[i];
+    }											/* { dg-error "expected" } */
+  [[omp::directive (parallel for reduction (inscan, +: a))]]				/* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan)]]								/* { dg-error "expected 'inclusive' or 'exclusive' clause before end of line" } */
+      a += c[i];
+    }
+  return a;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-8.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-8.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-8.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-9.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-9.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-9.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-10.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-10.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,192 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x -ffat-lto-objects -fdump-tree-gimple" } */
+
+extern void abort ();
+
+[[omp::directive (declare simd, linear (l))]] extern int f1 (int l);
+extern int f2 (int), f3 [[omp::directive (declare simd, uniform (m))]] (int m), f4 (int), z;
+[[omp::directive (declare simd, linear (l), simdlen(4))]] extern int f5 [[omp::directive (declare simd uniform (l) simdlen (8) notinbranch)]] (int l);
+
+int
+f1 (int l)
+{
+  return l;
+}
+
+/* { dg-final { scan-assembler-times "_ZGVbM4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM16l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN16l_f1:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f2 (int l)
+{
+  return l + 1;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f2:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f3 (int l)
+{
+  return l + 2;
+}
+
+/* { dg-final { scan-assembler-times "_ZGVbM4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM16u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN16u_f3:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f4 (int l)
+{
+  return l + 3;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f4:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f5 (int l)
+{	/* { dg-warning "GCC does not currently support simdlen 8 for type 'int'" "" { target aarch64*-*-* } .-1 } */
+  return l + 4;
+}
+
+/* { dg-final { scan-assembler-times "_ZGVbM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN4l_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN8u_f5:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]M8u_f5:" { target { i?86-*-* x86_64-*-* } } } } */
+
+[[omp::directive (declare simd, linear (l), simdlen(4), notinbranch),
+  omp::directive (declare simd, uniform (l), simdlen(4), inbranch)]]
+int
+f6 [[omp::sequence (directive (declare simd uniform (l) simdlen (8), notinbranch),
+		    omp::directive (declare simd linear (l) simdlen (8) inbranch))]] (int l)
+{	/* { dg-warning "GCC does not currently support simdlen 8 for type 'int'" "" { target aarch64*-*-* } .-2 } */
+  return l + 5;
+}
+
+/* { dg-final { scan-assembler-times "_ZGVbM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM4u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN4l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM8l_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN8u_f6:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]M4l_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]N4u_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]M8u_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-not "_ZGV\[bcde]N8l_f6:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f7 (int l)
+{
+  return l + 6;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f7:" { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f8 (int l)
+{
+  return l + 7;
+}
+
+/* { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]_f8:" { target { i?86-*-* x86_64-*-* } } } } */
+
+[[omp::sequence (omp::directive (declare variant (f7), match (construct={parallel})),
+		 directive (declare simd uniform (l), simdlen(4)))]]
+int
+f9 [[omp::directive (declare simd uniform (l) simdlen (8)),
+     omp::directive (declare variant (f8) match (construct={parallel,for}))]] (int l)
+{	/* { dg-warning "GCC does not currently support simdlen 8 for type 'int'" "" { target aarch64*-*-* } .-2 } */
+  return l + 8;
+}
+
+/* { dg-final { scan-assembler-times "_ZGVbM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN4u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVbN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN8u_f9:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int z;
+
+void
+test ()
+{
+  [[omp::directive (parallel)]]
+  if (f9 (3) != 9)
+    abort ();
+  [[omp::directive (parallel for)]]
+  for (int i = 0; i < 1; i++)
+    if (f9 (4) != 11)
+      abort ();
+  if (f9 (5) != 13)
+    abort ();
+}
+
+/* { dg-final { scan-tree-dump-times " = f7 \\\(3\\\);" 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times " = f8 \\\(4\\\);" 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times " = f9 \\\(5\\\);" 1 "gimple" } } */
+
+int
+f10 (int x)
+{
+  return x;
+}
+
+[[omp::directive (declare simd, notinbranch)]] int f10 (int);
+
+/* { dg-final { scan-assembler-times "_ZGVbN4v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcN4v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdN8v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeN16v_f10:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+
+int
+f11 (int x)
+{
+  return x + 1;
+}
+
+int f11 [[omp::directive (declare simd inbranch linear(x))]] (int x);
+
+/* { dg-final { scan-assembler-times "_ZGVbM4l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVcM4l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVdM8l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "_ZGVeM16l_f11:" 1 { target { i?86-*-* x86_64-*-* } } } } */
--- gcc/testsuite/gcc.dg/gomp/attrs-11.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-11.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,88 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x -Wno-attributes" } */
+
+void
+foo ()
+{
+  [[omp::directive (parallel)]] __asm ("");
+  __extension__ [[omp::directive (parallel)]] asm ("");		/* { dg-error "expected" } */
+  [[omp::directive (parallel)]] __label__ foo;			/* { dg-error "expected" } */
+  [[omp::directive (parallel)]] static_assert (true, "");	/* { dg-error "expected" } */
+  [[omp::directive (parallel)]] int a = 5;			/* { dg-error "not allowed to be specified in this context" } */
+  int b = 0;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wattributes"
+  [[omp::directive (parallel)]] l: b++;				/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+  switch (0)
+    {
+      [[omp::directive (parallel)]] case 6: break;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+      [[omp::directive (parallel)]] default: break;		/* { dg-warning "'omp::directive' scoped attribute directive ignored" } */
+    }
+#pragma GCC diagnostic pop
+}
+
+void
+bar ()
+{
+  [[omp::directive (declare simd)]] int a;		/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+  [[omp::directive (declare simd)]] int b, f1 (int);	/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+  [[omp::directive (declare simd)]] int f2 (int), c;	/* { dg-error "not immediately followed by a function declaration or definition" } */
+  int d [[omp::directive (declare simd)]];		/* { dg-error "not immediately followed by a function declaration or definition" } */
+  int f3 [[omp::directive (declare simd)]] (int), f4 [[omp::directive (declare simd)]] (int);
+  __extension__ [[omp::directive (declare simd)]] int f5 (int);
+  __extension__ int f6 [[omp::directive (declare simd, notinbranch)]] (int);
+  #pragma omp declare simd notinbranch
+  [[omp::directive (declare simd inbranch)]] int f7 (int);	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same declaration" } */
+  [[omp::directive (declare simd notinbranch)]]
+  #pragma omp declare simd inbranch	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  int f8 (int);
+  static int t1, t2, t3, t4;
+  [[omp::directive (declare simd), omp::directive (foobar)]] int f9 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  [[omp::directive (foobar), omp::directive (declare simd)]] int f10 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  [[omp::directive (threadprivate (t1)), omp::directive (declare simd)]] int f10 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  [[omp::directive (declare simd), omp::directive (threadprivate (t2))]] int f11 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  int f12 [[omp::directive (declare simd), omp::directive (foobar)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  int f13 [[omp::directive (foobar), omp::directive (declare simd)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+  int f14 [[omp::directive (threadprivate (t3)), omp::directive (declare simd)]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+  int f15 [[omp::directive (declare simd), omp::directive (threadprivate (t4))]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+}
+
+[[omp::directive (declare simd)]] int a;		/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+[[omp::directive (declare simd)]] int b, f16 (int);	/* { dg-error "not allowed to be specified in this context|not immediately followed by a function declaration or definition" } */
+[[omp::directive (declare simd)]] int f17 (int), c;	/* { dg-error "not immediately followed by a function declaration or definition" } */
+int d [[omp::directive (declare simd)]];		/* { dg-error "not immediately followed by a function declaration or definition" } */
+int f18 [[omp::directive (declare simd)]] (int), f19 [[omp::directive (declare simd)]] (int);
+__extension__ [[omp::directive (declare simd)]] int f20 (int);
+__extension__ int f21 [[omp::directive (declare simd, notinbranch)]] (int);
+#pragma omp declare simd notinbranch
+[[omp::directive (declare simd inbranch)]] int f22 (int);	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same declaration" } */
+[[omp::directive (declare simd notinbranch)]]
+#pragma omp declare simd inbranch	/* { dg-error "before '#pragma'" } */
+int f23 (int);				/* { dg-error "not immediately followed by a function declaration or definition" "" { target *-*-* } .-1 } */
+int t5, t6, t7, t8;
+[[omp::directive (declare simd), omp::directive (foobar)]] int f24 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+[[omp::directive (foobar), omp::directive (declare simd)]] int f25 (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+[[omp::directive (threadprivate (t5)), omp::directive (declare simd)]] int f26 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+[[omp::directive (declare simd), omp::directive (threadprivate (t6))]] int f27 (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+int f28 [[omp::directive (declare simd), omp::directive (foobar)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+int f29 [[omp::directive (foobar), omp::directive (declare simd)]] (int);	/* { dg-error "unknown OpenMP directive name" } */
+										/* { dg-error "'omp::directive' not allowed to be specified in this context" "" { target *-*-* } .-1 } */
+int f30 [[omp::directive (threadprivate (t7)), omp::directive (declare simd)]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+int f31 [[omp::directive (declare simd), omp::directive (threadprivate (t8))]] (int);	/* { dg-error "'omp::directive' not allowed to be specified in this context" } */
+
+void
+baz ()
+{
+  #pragma omp parallel
+  [[omp::directive (declare simd)]];				/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  [[omp::directive (parallel)]]
+  #pragma omp declare simd	/* { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } */
+  extern int f34 (int);
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-12.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-12.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#pragma omp declare target
+#pragma omp declare target
+[[omp::directive (declare target)]];
+int a;
+[[omp::directive (end declare target)]];
+#pragma omp end declare target
+#pragma omp end declare target
+[[omp::directive (declare target)]];
+int b;
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+int c;
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp declare target
+[[omp::directive (declare target)]];
+int d;
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+int e;
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp end declare target
+[[omp::directive (declare target)]];
+[[omp::directive (declare target)]];
+int f;
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+int g;
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+[[omp::directive (end declare target)]];
+[[omp::directive (declare target)]];
+#pragma omp declare target
+int h;
+#pragma omp end declare target
+#pragma omp end declare target		/* { dg-error "'declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp declare target
+[[omp::directive (declare target)]];
+int i;
+[[omp::directive (end declare target)]];
+[[omp::directive (end declare target)]];/* { dg-error "'declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
--- gcc/testsuite/gcc.dg/gomp/attrs-13.c.jj	2023-11-02 12:21:36.550631638 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-13.c	2023-11-02 12:21:36.550631638 +0100
@@ -0,0 +1,35 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+[[omp::directive(error)]];			/* { dg-error "'pragma omp error' encountered" } */
+[[omp::directive(error, at(compilation))]];	/* { dg-error "'pragma omp error' encountered" } */
+[[omp::directive(error severity(fatal))]];	/* { dg-error "'pragma omp error' encountered" } */
+[[omp::directive(error, message("my msg"))]];	/* { dg-error "'pragma omp error' encountered: my msg" } */
+[[omp::directive(error severity(warning)message("another message")at(compilation))]];	/* { dg-warning "'pragma omp error' encountered: another message" } */
+
+int
+foo (int i, int x)
+{
+  [[omp::directive(error)]];			/* { dg-error "'pragma omp error' encountered" } */
+  [[omp::directive(error, at(compilation))]];	/* { dg-error "'pragma omp error' encountered" } */
+  [[omp::directive(error severity(fatal))]];	/* { dg-error "'pragma omp error' encountered" } */
+  [[omp::directive(error, message("42 / 1"))]];	/* { dg-error "'pragma omp error' encountered: 42 / 1" } */
+  [[omp::directive(error severity(warning) message("bar") at(compilation))]];	/* { dg-warning "'pragma omp error' encountered: bar" } */
+  if (x)
+    [[omp::directive(error)]];			/* { dg-error "'pragma omp error' encountered" } */
+  i++;
+  if (x)
+    ;
+  else
+    [[omp::directive(error at(compilation))]];	/* { dg-error "'pragma omp error' encountered" } */
+  i++;
+  switch (0)
+    [[omp::directive(error, severity(fatal))]];	/* { dg-error "'pragma omp error' encountered" } */
+  while (0)
+    [[omp::directive(error, message("42 - 1"))]];	/* { dg-error "'pragma omp error' encountered: 42 - 1" } */
+  i++;
+  lab:
+  [[omp::directive(error, severity(warning) message("bar"), at(compilation))]];	/* { dg-warning "'pragma omp error' encountered: bar" } */
+  i++;
+  return i;
+}
--- gcc/testsuite/gcc.dg/gomp/attrs-14.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-14.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,5 @@ 
+/* PR c++/102413 */
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+[[omp::directive(error]];	/* { dg-error "expected|declare" } */
--- gcc/testsuite/gcc.dg/gomp/attrs-15.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-15.c	2023-11-02 12:22:08.283188503 +0100
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#pragma omp begin assumes absent (target)
+#pragma omp begin assumes absent (target)
+[[omp::directive (begin assumes absent (target))]];
+int a;
+[[omp::directive (end assumes)]];
+#pragma omp end assumes
+#pragma omp end assumes
+[[omp::directive (begin assumes absent (target))]];
+int b;
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" } */
+#pragma omp begin assumes absent (target)
+int c;
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" } */
+#pragma omp begin assumes absent (target)
+[[omp::directive (begin assumes absent (target))]];
+int d;
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" } */
+#pragma omp begin assumes absent (target)
+int e;
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" } */
+#pragma omp end assumes
+[[omp::directive (begin assumes absent (target))]];
+[[omp::directive (begin assumes absent (target))]];
+int f;
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" } */
+#pragma omp begin assumes absent (target)
+int g;
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" } */
+[[omp::directive (end assumes)]];
+[[omp::directive (begin assumes absent (target))]];
+#pragma omp begin assumes absent (target)
+int h;
+#pragma omp end assumes
+#pragma omp end assumes		/* { dg-error "'begin assumes' in attribute syntax terminated with 'end assumes' in pragma syntax" } */
+#pragma omp begin assumes absent (target)
+[[omp::directive (begin assumes absent (target))]];
+int i;
+[[omp::directive (end assumes)]];
+[[omp::directive (end assumes)]];/* { dg-error "'begin assumes' in pragma syntax terminated with 'end assumes' in attribute syntax" } */
--- gcc/testsuite/gcc.dg/gomp/attrs-16.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-16.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-16.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-17.c.jj	2023-11-02 12:21:36.552631610 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-17.c	2023-11-02 12:21:36.552631610 +0100
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#include "../../g++.dg/gomp/attrs-17.C"
--- gcc/testsuite/gcc.dg/gomp/attrs-18.c.jj	2023-11-02 12:21:36.551631624 +0100
+++ gcc/testsuite/gcc.dg/gomp/attrs-18.c	2023-11-02 12:21:36.551631624 +0100
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -std=c2x" } */
+
+#pragma omp begin declare target
+#pragma omp begin declare target device_type (any)
+[[omp::directive (begin declare target, device_type (host))]];
+int a;
+[[omp::directive (end declare target)]];
+#pragma omp end declare target
+#pragma omp end declare target
+[[omp::directive (begin declare target device_type (nohost))]];
+int b;
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+int c;
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp begin declare target device_type (host)
+[[omp::directive (begin declare target)]];
+int d;
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+int e;
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+#pragma omp end declare target
+[[omp::directive (begin declare target device_type (any))]];
+[[omp::directive (begin declare target)]];
+int f;
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+int g;
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
+[[omp::directive (end declare target)]];
+[[omp::directive (begin declare target)]];
+#pragma omp begin declare target
+int h;
+#pragma omp end declare target
+#pragma omp end declare target		/* { dg-error "'begin declare target' in attribute syntax terminated with 'end declare target' in pragma syntax" } */
+#pragma omp begin declare target
+[[omp::directive (begin declare target)]];
+int i;
+[[omp::directive (end declare target)]];
+[[omp::directive (end declare target)]];/* { dg-error "'begin declare target' in pragma syntax terminated with 'end declare target' in attribute syntax" } */
--- gcc/testsuite/g++.dg/gomp/attrs-2.C.jj	2023-11-02 07:48:27.984549311 +0100
+++ gcc/testsuite/g++.dg/gomp/attrs-2.C	2023-11-02 12:21:36.550631638 +0100
@@ -1,4 +1,4 @@ 
-// { dg-do compile { target c++17 } }
+// { dg-do compile { target c++11 } }
 
 typedef enum omp_allocator_handle_t
 : __UINTPTR_TYPE__
@@ -162,7 +162,11 @@  bar (int d, int m, int i1, int i2, int i
     private (p),firstprivate (f),if (parallel: i2),default(shared),shared(s),copyin(t),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),allocate (f))]]
   {
+#if __cplusplus >= 201703L
     [[using omp:directive (section)]]
+#else
+    [[omp::directive (section)]]
+#endif
     {}
     [[omp::sequence (omp::directive (section))]]
     {}
@@ -176,11 +180,20 @@  bar (int d, int m, int i1, int i2, int i
     {}
   }
   [[omp::directive (barrier)]];
+#if __cplusplus >= 201703L
   [[using omp:sequence (omp::directive (single, private (p),firstprivate (f),allocate (f),nowait))]]
+#else
+  [[omp::sequence (omp::directive (single, private (p),firstprivate (f),allocate (f),nowait))]]
+#endif
     ;
   [[omp::sequence (directive (barrier))]];
+#if __cplusplus >= 201703L
   [[using omp:sequence (directive (parallel, private (p)),
     omp::directive (single, copyprivate (p),firstprivate (f),allocate (f)))]]
+#else
+  [[omp::sequence (directive (parallel, private (p)),
+    omp::directive (single, copyprivate (p),firstprivate (f),allocate (f)))]]
+#endif
     p = 6;
   [[omp::directive (target parallel,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
@@ -194,7 +207,13 @@  bar (int d, int m, int i1, int i2, int i
     allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:directive (target parallel for,
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),order(concurrent),
@@ -209,12 +228,24 @@  bar (int d, int m, int i1, int i2, int i
     allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (directive (target teams,
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (target teams,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
     ;
-  [[using omp:sequence (directive (target,
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (target,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr(hda)))]]
     ;
@@ -266,7 +297,13 @@  bar (int d, int m, int i1, int i2, int i
     order(concurrent),allocate (f)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (omp::directive (taskgroup, task_reduction(+:r), allocate (r)),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (omp::directive (taskgroup, task_reduction(+:r), allocate (r)),
     directive (taskloop simd,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied,if(i1),final(fi),mergeable,nogroup,priority (pp),
     safelen(8),simdlen(4),linear(ll: 1),aligned(q: 32),in_reduction(+:r),nontemporal(ntm),
@@ -321,7 +358,13 @@  bar (int d, int m, int i1, int i2, int i
     lastprivate (l),schedule(static, 4),order(concurrent),allocate (omp_default_mem_alloc: f)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (directive (target),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (target),
     directive (teams distribute parallel for simd,
     private(p),firstprivate (f),shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),
     collapse(1),dist_schedule(static, 16),
@@ -389,21 +432,39 @@  bar (int d, int m, int i1, int i2, int i
     private (p),firstprivate (f),if (parallel: i2),default(shared),shared(s),reduction(+:r),
     num_threads (nth),proc_bind(spread),copyin(t),allocate (f))]]
     ;
-  [[using omp:sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
     omp::directive (master taskloop,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied, if(taskloop: i1),final(fi),mergeable, priority (pp),
     reduction(default, +:r),in_reduction(+:r2),allocate (f)))]]
   for (int i = 0; i < 64; i++)
     ll++;
-  [[using omp:sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp::
+#endif
+    sequence (directive (taskgroup, task_reduction (+:r2),allocate (r2)),
     omp::directive (masked taskloop,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied, if(taskloop: i1),final(fi),mergeable, priority (pp),
     reduction(default, +:r),in_reduction(+:r2),allocate (f),filter(d)))]]
   for (int i = 0; i < 64; i++)
     ll++;
+#if __cplusplus >= 201703L
   [[using omp:directive (master)]];
   [[using omp:directive (masked)]];
   [[using omp:directive (masked,filter(d))]];
+#else
+  [[omp::directive (master)]];
+  [[omp::directive (masked)]];
+  [[omp::directive (masked,filter(d))]];
+#endif
   [[omp::sequence (omp::directive (taskgroup task_reduction (+:r2),allocate (r2)),
     directive (master taskloop simd,
     private (p),firstprivate (f),lastprivate (l),shared (s),default(shared),grainsize (g),collapse(1),untied,if(taskloop: i1),if(simd: i2),final(fi),mergeable,priority (pp),
@@ -544,7 +605,11 @@  bar (int d, int m, int i1, int i2, int i
   }
   [[omp::directive (critical (foobar),hint(omp_sync_hint_none))]]
   ;
+#if __cplusplus >= 201703L
   [[using omp:directive (taskwait, depend (inout: dd[0]))]]
+#else
+  [[omp::directive (taskwait, depend (inout: dd[0]))]]
+#endif
   ;
   [[omp::directive (taskgroup, task_reduction(+:r2),allocate (r2))]]
   ;
@@ -596,7 +661,13 @@  bar (int d, int m, int i1, int i2, int i
   [[omp::directive (scope, private (p), firstprivate (f), reduction(+:r), nowait,
     allocate(omp_default_mem_alloc: r))]]
     ;
-  [[using omp:directive (scope, private (p), firstprivate (f), reduction(task, +:r),
+  [[
+#if __cplusplus >= 201703L
+    using omp:
+#else
+    omp ::
+#endif
+    directive (scope, private (p), firstprivate (f), reduction(task, +:r),
     allocate (omp_default_mem_alloc: f))]]
     ;
   extern int t2;
@@ -632,8 +703,13 @@  corge ()
   [[omp::directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
     omp::directive (declare simd,simdlen(8),notinbranch)]]
   extern int corge3 (int l, int *p);
+#if __cplusplus >= 201703L
   [[using omp:directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
     directive (declare simd, simdlen(8),notinbranch)]]
+#else
+  [[omp::directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
+    omp :: directive (declare simd, simdlen(8),notinbranch)]]
+#endif
   extern int corge4 (int l, int *p);
   [[omp::sequence (directive (declare simd, simdlen(4),linear(l),aligned(p:4),uniform(p),inbranch),
     omp::directive (declare simd, simdlen(8),notinbranch))]]
@@ -658,7 +734,11 @@  garply (int a, int *c, int *d, int *e, i
   for (i = 0; i < 64; i++)
     {
       a += c[i];
+#if __cplusplus >= 201703L
       [[using omp : sequence (sequence (directive (scan inclusive (a))))]]
+#else
+      [[omp:: sequence (sequence (directive (scan inclusive (a))))]]
+#endif
       d[i] = a;
     }
   return a;