--- gcc/c-family/c-common.h.jj	2019-10-09 10:27:12.938397370 +0200
+++ gcc/c-family/c-common.h	2019-10-09 17:11:52.348385875 +0200
@@ -1189,6 +1189,8 @@ extern tree c_omp_declare_simd_clauses_t
 extern void c_omp_declare_simd_clauses_to_decls (tree, tree);
 extern bool c_omp_predefined_variable (tree);
 extern enum omp_clause_default_kind c_omp_predetermined_sharing (tree);
+extern tree c_omp_check_context_selector (location_t, tree);
+extern tree c_omp_get_context_selector (tree, const char *, const char *);
 
 /* Return next tree in the chain for chain_next walking of tree nodes.  */
 static inline tree
--- gcc/c-family/c-omp.c.jj	2019-10-09 10:27:12.877398287 +0200
+++ gcc/c-family/c-omp.c	2019-10-09 17:10:47.574357967 +0200
@@ -2011,7 +2011,7 @@ c_omp_declare_simd_clauses_to_numbers (t
 	  if (arg == NULL_TREE)
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD is not an function argument", decl);
+			"%qD is not a function argument", decl);
 	      continue;
 	    }
 	  OMP_CLAUSE_DECL (c) = build_int_cst (integer_type_node, idx);
@@ -2026,7 +2026,7 @@ c_omp_declare_simd_clauses_to_numbers (t
 	      if (arg == NULL_TREE)
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qD is not an function argument", decl);
+			    "%qD is not a function argument", decl);
 		  continue;
 		}
 	      OMP_CLAUSE_LINEAR_STEP (c)
@@ -2120,3 +2120,133 @@ c_omp_predetermined_sharing (tree decl)
 
   return OMP_CLAUSE_DEFAULT_UNSPECIFIED;
 }
+
+/* Diagnose errors in an OpenMP context selector, return CTX if
+   it is correct or error_mark_node otherwise.  */
+
+tree
+c_omp_check_context_selector (location_t loc, tree ctx)
+{
+  /* Each trait-set-selector-name can only be specified once.
+     There are just 4 set names.  */
+  for (tree t1 = ctx; t1; t1 = TREE_CHAIN (t1))
+    for (tree t2 = TREE_CHAIN (t1); t2; t2 = TREE_CHAIN (t2))
+      if (TREE_PURPOSE (t1) == TREE_PURPOSE (t2))
+	{
+	  error_at (loc, "selector set %qs specified more than once",
+	  	    IDENTIFIER_POINTER (TREE_PURPOSE (t1)));
+	  return error_mark_node;
+	}
+  for (tree t = ctx; t; t = TREE_CHAIN (t))
+    {
+      /* Each trait-selector-name can only be specified once.  */
+      if (list_length (TREE_VALUE (t)) < 5)
+	{
+	  for (tree t1 = TREE_VALUE (t); t1; t1 = TREE_CHAIN (t1))
+	    for (tree t2 = TREE_CHAIN (t1); t2; t2 = TREE_CHAIN (t2))
+	      if (TREE_PURPOSE (t1) == TREE_PURPOSE (t2))
+		{
+		  error_at (loc,
+			    "selector %qs specified more than once in set %qs",
+			    IDENTIFIER_POINTER (TREE_PURPOSE (t1)),
+			    IDENTIFIER_POINTER (TREE_PURPOSE (t)));
+		  return error_mark_node;
+		}
+	}
+      else
+	{
+	  hash_set<tree> pset;
+	  for (tree t1 = TREE_VALUE (t); t1; t1 = TREE_CHAIN (t1))
+	    if (pset.add (TREE_PURPOSE (t1)))
+	      {
+		error_at (loc,
+			  "selector %qs specified more than once in set %qs",
+			  IDENTIFIER_POINTER (TREE_PURPOSE (t1)),
+			  IDENTIFIER_POINTER (TREE_PURPOSE (t)));
+		return error_mark_node;
+	      }
+	}
+
+      static const char *const kind[] = {
+	"host", "nohost", "cpu", "gpu", "fpga", "any", NULL };
+      static const char *const vendor[] = {
+	"amd", "arm", "bsc", "cray", "fujitsu", "gnu", "ibm", "intel",
+	"llvm", "pgi", "ti", "unknown", NULL };
+      static const char *const extension[] = { NULL };
+      static const char *const atomic_default_mem_order[] = {
+	"seq_cst", "relaxed", "acq_rel", NULL };
+      struct known_properties { const char *set; const char *selector;
+				const char *const *props; };
+      known_properties props[] = {
+	{ "device", "kind", kind },
+	{ "implementation", "vendor", vendor },
+	{ "implementation", "extension", extension },
+	{ "implementation", "atomic_default_mem_order",
+	  atomic_default_mem_order } };
+      for (tree t1 = TREE_VALUE (t); t1; t1 = TREE_CHAIN (t1))
+	for (unsigned i = 0; i < ARRAY_SIZE (props); i++)
+	  if (!strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t1)),
+					   props[i].selector)
+	      && !strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t)),
+					      props[i].set))
+	    for (tree t2 = TREE_VALUE (t1); t2; t2 = TREE_CHAIN (t2))
+	      for (unsigned j = 0; ; j++)
+		{
+		  if (props[i].props[j] == NULL)
+		    {
+		      if (!strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t2)),
+				   " score"))
+			break;
+		      if (props[i].props == atomic_default_mem_order)
+			{
+			  error_at (loc,
+				    "incorrect property %qs of %qs selector",
+				    IDENTIFIER_POINTER (TREE_PURPOSE (t2)),
+				    "atomic_default_mem_order");
+			  return error_mark_node;
+			}
+		      else
+			warning_at (loc, 0,
+				    "unknown property %qs of %qs selector",
+				    IDENTIFIER_POINTER (TREE_PURPOSE (t2)),
+				    props[i].selector);
+		      break;
+		    }
+		  else if (!strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t2)),
+				    props[i].props[j]))
+		    {
+		      if (props[i].props == atomic_default_mem_order
+			  && t2 != TREE_VALUE (t1))
+			{
+			  tree t3 = TREE_VALUE (t1);
+			  if (!strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t3)),
+				       " score")
+			      && t2 == TREE_CHAIN (TREE_VALUE (t1)))
+			    break;
+			  error_at (loc,
+				    "%qs selector must have a single property",
+				    "atomic_default_mem_order");
+			  return error_mark_node;
+			}
+		      break;
+		    }
+		}
+    }
+  return ctx;
+}
+
+/* From context selector CTX, return trait-selector with name SEL in
+   trait-selector-set with name SET if any, or NULL_TREE if not found.  */
+
+tree
+c_omp_get_context_selector (tree ctx, const char *set, const char *sel)
+{
+  tree setid = get_identifier (set);
+  tree selid = get_identifier (sel);
+  for (tree t1 = ctx; t1; t1 = TREE_CHAIN (t1))
+    if (TREE_PURPOSE (t1) == setid)
+      for (tree t2 = TREE_VALUE (t1); t2; t2 = TREE_CHAIN (t2))
+	if (TREE_PURPOSE (t2) == selid)
+	  return t2;
+  return NULL_TREE;
+}
--- gcc/c-family/c-attribs.c.jj	2019-10-09 10:27:12.888398122 +0200
+++ gcc/c-family/c-attribs.c	2019-10-09 13:06:29.517475009 +0200
@@ -140,6 +140,8 @@ static tree handle_warn_unused_attribute
 static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *);
 static tree handle_omp_declare_simd_attribute (tree *, tree, tree, int,
 					       bool *);
+static tree handle_omp_declare_variant_attribute (tree *, tree, tree, int,
+						  bool *);
 static tree handle_simd_attribute (tree *, tree, tree, int, bool *);
 static tree handle_omp_declare_target_attribute (tree *, tree, tree, int,
 						 bool *);
@@ -442,6 +444,8 @@ const struct attribute_spec c_common_att
 			      handle_returns_nonnull_attribute, NULL },
   { "omp declare simd",       0, -1, true,  false, false, false,
 			      handle_omp_declare_simd_attribute, NULL },
+  { "omp declare variant",    0, -1, true,  false, false, false,
+			      handle_omp_declare_variant_attribute, NULL },
   { "simd",		      0, 1, true,  false, false, false,
 			      handle_simd_attribute, NULL },
   { "omp declare target",     0, -1, true, false, false, false,
@@ -3063,6 +3067,15 @@ handle_omp_declare_simd_attribute (tree
 {
   return NULL_TREE;
 }
+
+/* Handle an "omp declare variant" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_omp_declare_variant_attribute (tree *, tree, tree, int, bool *)
+{
+  return NULL_TREE;
+}
 
 /* Handle a "simd" attribute.  */
 
--- gcc/c/c-parser.c.jj	2019-10-09 10:27:10.898428044 +0200
+++ gcc/c/c-parser.c	2019-10-09 17:34:49.773694607 +0200
@@ -15213,11 +15213,15 @@ c_parser_oacc_all_clauses (c_parser *par
 }
 
 /* Parse all OpenMP clauses.  The set clauses allowed by the directive
-   is a bitmask in MASK.  Return the list of clauses found.  */
+   is a bitmask in MASK.  Return the list of clauses found.
+   FINISH_P set if c_finish_omp_clauses should be called.
+   NESTED_P set if clauses should be terminated by closing paren instead
+   of end of pragma.  */
 
 static tree
 c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
-			  const char *where, bool finish_p = true)
+			  const char *where, bool finish_p = true,
+			  bool nested_p = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -15229,6 +15233,9 @@ c_parser_omp_all_clauses (c_parser *pars
       const char *c_name;
       tree prev = clauses;
 
+      if (nested_p && c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	break;
+
       if (!first && c_parser_next_token_is (parser, CPP_COMMA))
 	c_parser_consume_token (parser);
 
@@ -15513,7 +15520,8 @@ c_parser_omp_all_clauses (c_parser *pars
     }
 
  saw_error:
-  c_parser_skip_to_pragma_eol (parser);
+  if (!nested_p)
+    c_parser_skip_to_pragma_eol (parser);
 
   if (finish_p)
     {
@@ -18919,7 +18927,11 @@ check_clauses:
 }
 
 /* OpenMP 4.0:
-   # pragma omp declare simd declare-simd-clauses[optseq] new-line  */
+   # pragma omp declare simd declare-simd-clauses[optseq] new-line
+
+   OpenMP 5.0:
+   # pragma omp declare variant (identifier) match(context-selector) new-line
+   */
 
 #define OMP_DECLARE_SIMD_CLAUSE_MASK				\
 	( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SIMDLEN)	\
@@ -18932,6 +18944,12 @@ check_clauses:
 static void
 c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context)
 {
+  c_token *token = c_parser_peek_token (parser);
+  gcc_assert (token->type == CPP_NAME);
+  tree kind = token->value;
+  gcc_assert (strcmp (IDENTIFIER_POINTER (kind), "simd") == 0
+	      || strcmp (IDENTIFIER_POINTER (kind), "variant") == 0);
+
   auto_vec<c_token> clauses;
   while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL))
     {
@@ -18949,17 +18967,14 @@ c_parser_omp_declare_simd (c_parser *par
 
   while (c_parser_next_token_is (parser, CPP_PRAGMA))
     {
-      if (c_parser_peek_token (parser)->pragma_kind
-	  != PRAGMA_OMP_DECLARE
+      if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_DECLARE
 	  || c_parser_peek_2nd_token (parser)->type != CPP_NAME
-	  || strcmp (IDENTIFIER_POINTER
-				(c_parser_peek_2nd_token (parser)->value),
-		     "simd") != 0)
+	  || c_parser_peek_2nd_token (parser)->value != kind)
 	{
-	  c_parser_error (parser,
-			  "%<#pragma omp declare simd%> must be followed by "
-			  "function declaration or definition or another "
-			  "%<#pragma omp declare simd%>");
+	  error ("%<#pragma omp declare %s%> must be followed by "
+		 "function declaration or definition or another "
+		 "%<#pragma omp declare %s%>",
+		 IDENTIFIER_POINTER (kind), IDENTIFIER_POINTER (kind));
 	  return;
 	}
       c_parser_consume_pragma (parser);
@@ -19007,8 +19022,9 @@ c_parser_omp_declare_simd (c_parser *par
     case pragma_struct:
     case pragma_param:
     case pragma_stmt:
-      c_parser_error (parser, "%<#pragma omp declare simd%> must be followed by "
-			      "function declaration or definition");
+      error ("%<#pragma omp declare %s%> must be followed by "
+	     "function declaration or definition",
+	     IDENTIFIER_POINTER (kind));
       break;
     case pragma_compound:
       if (c_parser_next_token_is (parser, CPP_KEYWORD)
@@ -19034,38 +19050,470 @@ c_parser_omp_declare_simd (c_parser *par
 					 NULL, clauses);
 	  break;
 	}
-      c_parser_error (parser, "%<#pragma omp declare simd%> must be followed by "
-			      "function declaration or definition");
+      error ("%<#pragma omp declare %s%> must be followed by "
+	     "function declaration or definition",
+	     IDENTIFIER_POINTER (kind));
       break;
     default:
       gcc_unreachable ();
     }
 }
 
-/* Finalize #pragma omp declare simd clauses after FNDECL has been parsed,
-   and put that into "omp declare simd" attribute.  */
+static const char *const omp_construct_selectors[] = {
+  "simd", "target", "teams", "parallel", "for", NULL };
+static const char *const omp_device_selectors[] = {
+  "kind", "isa", "arch", NULL };
+static const char *const omp_implementation_selectors[] = {
+  "vendor", "extension", "atomic_default_mem_order", "unified_address",
+  "unified_shared_memory", "dynamic_allocators", "reverse_offload", NULL };
+static const char *const omp_user_selectors[] = {
+  "condition", NULL };
+
+/* OpenMP 5.0:
+
+   trait-selector:
+     trait-selector-name[([trait-score:]trait-property[,trait-property[,...]])]
+
+   trait-score:
+     score(score-expression)  */
+
+static tree
+c_parser_omp_context_selector (c_parser *parser, tree set, tree parms)
+{
+  tree ret = NULL_TREE;
+  do
+    {
+      tree selector;
+      if (c_parser_next_token_is (parser, CPP_KEYWORD)
+	  || c_parser_next_token_is (parser, CPP_NAME))
+	selector = c_parser_peek_token (parser)->value;
+      else
+	{
+	  c_parser_error (parser, "expected trait selector name");
+	  return error_mark_node;
+	}
+
+      tree properties = NULL_TREE;
+      const char *const *selectors = NULL;
+      bool allow_score = true;
+      bool allow_user = false;
+      int property_limit = 0;
+      enum { CTX_PROPERTY_NONE, CTX_PROPERTY_USER, CTX_PROPERTY_IDLIST,
+	     CTX_PROPERTY_EXPR, CTX_PROPERTY_SIMD } property_kind
+	= CTX_PROPERTY_NONE;
+      switch (IDENTIFIER_POINTER (set)[0])
+	{
+	case 'c': /* construct */
+	  selectors = omp_construct_selectors;
+	  allow_score = false;
+	  property_limit = 1;
+	  property_kind = CTX_PROPERTY_SIMD;
+	  break;
+	case 'd': /* device */
+	  selectors = omp_device_selectors;
+	  allow_score = false;
+	  allow_user = true;
+	  property_limit = 3;
+	  property_kind = CTX_PROPERTY_IDLIST;
+	  break;
+	case 'i': /* implementation */
+	  selectors = omp_implementation_selectors;
+	  allow_user = true;
+	  property_limit = 3;
+	  property_kind = CTX_PROPERTY_IDLIST;
+	  break;
+	case 'u': /* user */
+	  selectors = omp_user_selectors;
+	  property_limit = 1;
+	  property_kind = CTX_PROPERTY_EXPR;
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      for (int i = 0; ; i++)
+	{
+	  if (selectors[i] == NULL)
+	    {
+	      if (allow_user)
+		{
+		  property_kind = CTX_PROPERTY_USER;
+		  break;
+		}
+	      else
+		{
+		  error_at (c_parser_peek_token (parser)->location,
+			    "selector %qs not allowed for context selector "
+			    "set %qs", IDENTIFIER_POINTER (selector),
+			    IDENTIFIER_POINTER (set));
+		  c_parser_consume_token (parser);
+		  return error_mark_node;
+		}
+	    }
+	  if (i == property_limit)
+	    property_kind = CTX_PROPERTY_NONE;
+	  if (strcmp (selectors[i], IDENTIFIER_POINTER (selector)) == 0)
+	    break;
+	}
+
+      c_parser_consume_token (parser);
+
+      if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+	{
+	  if (property_kind == CTX_PROPERTY_NONE)
+	    {
+	      error_at (c_parser_peek_token (parser)->location,
+			"selector %qs does not accept any properties",
+			IDENTIFIER_POINTER (selector));
+	      return error_mark_node;
+	    }
+
+	  matching_parens parens;
+	  parens.require_open (parser);
+
+	  c_token *token = c_parser_peek_token (parser);
+	  if (allow_score
+	      && c_parser_next_token_is (parser, CPP_NAME)
+	      && strcmp (IDENTIFIER_POINTER (token->value), "score") == 0
+	      && c_parser_peek_2nd_token (parser)->type == CPP_OPEN_PAREN)
+	    {
+	      c_parser_consume_token (parser);
+
+	      matching_parens parens2;
+	      parens2.require_open (parser);
+	      tree score = c_parser_expr_no_commas (parser, NULL).value;
+	      parens2.skip_until_found_close (parser);
+	      c_parser_require (parser, CPP_COLON, "expected %<:%>");
+	      if (score != error_mark_node)
+		{
+		  mark_exp_read (score);
+		  score = c_fully_fold (score, false, NULL);
+		  if (!INTEGRAL_TYPE_P (TREE_TYPE (score))
+		      || !tree_fits_shwi_p (score))
+		    error_at (token->location, "score argument must be "
+			      "constant integer expression");
+		  else
+		    properties = tree_cons (get_identifier (" score"),
+					    score, properties);
+		}
+	      token = c_parser_peek_token (parser);
+	    }
+
+	  switch (property_kind)
+	    {
+	      tree t;
+	    case CTX_PROPERTY_USER:
+	      do
+		{
+		  t = c_parser_expr_no_commas (parser, NULL).value;
+		  if (TREE_CODE (t) == STRING_CST)
+		    properties = tree_cons (NULL_TREE, t, properties);
+		  else if (t != error_mark_node)
+		    {
+		      mark_exp_read (t);
+		      t = c_fully_fold (t, false, NULL);
+		      if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+			  || !tree_fits_shwi_p (t))
+			error_at (token->location, "property must be "
+				  "constant integer expression or string "
+				  "literal");
+		      else
+			properties = tree_cons (NULL_TREE, t, properties);
+		    }
+
+		  if (c_parser_next_token_is (parser, CPP_COMMA))
+		    c_parser_consume_token (parser);
+		  else
+		    break;
+		}
+	      while (1);
+	      break;
+	    case CTX_PROPERTY_IDLIST:
+	      do
+		{
+		  tree prop;
+		  if (c_parser_next_token_is (parser, CPP_KEYWORD)
+		      || c_parser_next_token_is (parser, CPP_NAME))
+		    prop = c_parser_peek_token (parser)->value;
+		  else
+		    {
+		      c_parser_error (parser, "expected identifier");
+		      return error_mark_node;
+		    }
+		  c_parser_consume_token (parser);
+
+		  properties = tree_cons (prop, NULL_TREE, properties);
+
+		  if (c_parser_next_token_is (parser, CPP_COMMA))
+		    c_parser_consume_token (parser);
+		  else
+		    break;
+		}
+	      while (1);
+	      break;
+	    case CTX_PROPERTY_EXPR:
+	      t = c_parser_expr_no_commas (parser, NULL).value;
+	      if (t != error_mark_node)
+		{
+		  mark_exp_read (t);
+		  t = c_fully_fold (t, false, NULL);
+		  if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+		      || !tree_fits_shwi_p (t))
+		    error_at (token->location, "property must be "
+			      "constant integer expression");
+		  else
+		    properties = tree_cons (NULL_TREE, t, properties);
+		}
+	      break;
+	    case CTX_PROPERTY_SIMD:
+	      if (parms == NULL_TREE)
+		{
+		  error_at (token->location, "properties for %<simd%> "
+			    "selector may not be specified in "
+			    "%<metadirective%>");
+		  return error_mark_node;
+		}
+	      tree c;
+	      c = c_parser_omp_all_clauses (parser,
+					    OMP_DECLARE_SIMD_CLAUSE_MASK,
+					    "simd", true, true);
+	      c = c_omp_declare_simd_clauses_to_numbers (parms
+							 == error_mark_node
+							 ? NULL_TREE : parms,
+							 c);
+	      properties = tree_cons (NULL_TREE, c, properties);
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+
+	  parens.skip_until_found_close (parser);
+	  properties = nreverse (properties);
+	}
+      else if (property_kind == CTX_PROPERTY_IDLIST
+	       || property_kind == CTX_PROPERTY_EXPR)
+	{
+	  c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>");
+	  return error_mark_node;
+	}
+
+      ret = tree_cons (selector, properties, ret);
+
+      if (c_parser_next_token_is (parser, CPP_COMMA))
+	c_parser_consume_token (parser);
+      else
+	break;
+    }
+  while (1);
+
+  return nreverse (ret);
+}
+
+/* OpenMP 5.0:
+
+   trait-set-selector[,trait-set-selector[,...]]
+
+   trait-set-selector:
+     trait-set-selector-name = { trait-selector[, trait-selector[, ...]] }
+
+   trait-set-selector-name:
+     constructor
+     device
+     implementation
+     user  */
+
+static tree
+c_parser_omp_context_selector_specification (c_parser *parser, tree parms)
+{
+  tree ret = NULL_TREE;
+  do
+    {
+      const char *setp = "";
+      if (c_parser_next_token_is (parser, CPP_NAME))
+	setp = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+      switch (setp[0])
+	{
+	case 'c':
+	  if (strcmp (setp, "construct") == 0)
+	    setp = NULL;
+	  break;
+	case 'd':
+	  if (strcmp (setp, "device") == 0)
+	    setp = NULL;
+	  break;
+	case 'i':
+	  if (strcmp (setp, "implementation") == 0)
+	    setp = NULL;
+	  break;
+	case 'u':
+	  if (strcmp (setp, "user") == 0)
+	    setp = NULL;
+	  break;
+	default:
+	  break;
+	}
+      if (setp)
+	{
+	  c_parser_error (parser, "expected %<construct%>, %<device%>, "
+				  "%<implementation%> or %<user%>");
+	  return error_mark_node;
+	}
+
+      tree set = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+
+      if (!c_parser_require (parser, CPP_EQ, "expected %<=%>"))
+	return error_mark_node;
+
+      matching_braces braces;
+      if (!braces.require_open (parser))
+	return error_mark_node;
+
+      tree selectors = c_parser_omp_context_selector (parser, set, parms);
+      if (selectors == error_mark_node)
+	ret = error_mark_node;
+      else if (ret != error_mark_node)
+	ret = tree_cons (set, selectors, ret);
+
+      braces.skip_until_found_close (parser);
+
+      if (c_parser_next_token_is (parser, CPP_COMMA))
+	c_parser_consume_token (parser);
+      else
+	break;
+    }
+  while (1);
+
+  if (ret == error_mark_node)
+    return ret;
+  return nreverse (ret);
+}
+
+/* Finalize #pragma omp declare variant after FNDECL has been parsed, and put
+   that into "omp declare variant" attribute.  */
+
+static void
+c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
+{
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    {
+     fail:
+      c_parser_skip_to_pragma_eol (parser, false);
+      return;
+    }
+
+  if (c_parser_next_token_is_not (parser, CPP_NAME)
+      || c_parser_peek_token (parser)->id_kind != C_ID_ID)
+    {
+      c_parser_error (parser, "expected identifier");
+      goto fail;
+    }
+
+  c_token *token = c_parser_peek_token (parser);
+  tree variant = lookup_name (token->value);
+
+  if (variant == NULL_TREE)
+    {
+      undeclared_variable (token->location, token->value);
+      variant = error_mark_node;
+    }
+
+  c_parser_consume_token (parser);
+
+  parens.require_close (parser);
+
+  const char *clause = "";
+  location_t match_loc = c_parser_peek_token (parser)->location;
+  if (c_parser_next_token_is (parser, CPP_NAME))
+    clause = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+  if (strcmp (clause, "match"))
+    {
+      c_parser_error (parser, "expected %<match%>");
+      goto fail;
+    }
+
+  c_parser_consume_token (parser);
+
+  if (!parens.require_open (parser))
+    goto fail;
+
+  if (parms == NULL_TREE)
+    parms = error_mark_node;
+
+  tree ctx = c_parser_omp_context_selector_specification (parser, parms);
+  if (ctx == error_mark_node)
+    goto fail;
+  ctx = c_omp_check_context_selector (match_loc, ctx);
+  if (ctx != error_mark_node && variant != error_mark_node)
+    {
+      if (TREE_CODE (variant) != FUNCTION_DECL)
+	{
+	  error_at (token->location, "variant %qD is not a function", variant);
+	  variant = error_mark_node;
+	}
+      else if (c_omp_get_context_selector (ctx, "construct", "simd")
+	       == NULL_TREE
+	       && !comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
+	{
+	  error_at (token->location, "variant %qD and base %qD have "
+				     "incompatible types", variant, fndecl);
+	  variant = error_mark_node;
+	}
+      else if (fndecl_built_in_p (variant)
+	       && (strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
+			    "__builtin_", strlen ("__builtin_")) == 0
+		   || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
+			       "__sync_", strlen ("__sync_")) == 0
+		   || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)),
+			       "__atomic_", strlen ("__atomic_")) == 0))
+	{
+	  error_at (token->location, "variant %qD is a built-in", variant);
+	  variant = error_mark_node;
+	}
+      if (variant != error_mark_node)
+	{
+	  C_DECL_USED (variant) = 1;
+	  tree attr = tree_cons (get_identifier ("omp declare variant"),
+				 build_tree_list (variant, ctx),
+				 DECL_ATTRIBUTES (fndecl));
+	  DECL_ATTRIBUTES (fndecl) = attr;
+	}
+    }
+
+  parens.require_close (parser);
+  c_parser_skip_to_pragma_eol (parser);
+}
+
+/* Finalize #pragma omp declare simd or #pragma omp declare variant
+   clauses after FNDECL has been parsed, and put that into "omp declare simd"
+   or "omp declare variant" attribute.  */
 
 static void
 c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms,
 			   vec<c_token> clauses)
 {
-  /* Normally first token is CPP_NAME "simd".  CPP_EOF there indicates
-     error has been reported and CPP_PRAGMA that c_finish_omp_declare_simd
-     has already processed the tokens.  */
+  /* Normally first token is CPP_NAME "simd" or "variant".  CPP_EOF there
+     indicates error has been reported and CPP_PRAGMA that
+     c_finish_omp_declare_simd has already processed the tokens.  */
   if (clauses.exists () && clauses[0].type == CPP_EOF)
     return;
+  const char *kind = "simd";
+  if (clauses.exists ()
+      && (clauses[0].type == CPP_NAME || clauses[0].type == CPP_PRAGMA))
+    kind = IDENTIFIER_POINTER (clauses[0].value);
+  gcc_assert (strcmp (kind, "simd") == 0 || strcmp (kind, "variant") == 0);
   if (fndecl == NULL_TREE || TREE_CODE (fndecl) != FUNCTION_DECL)
     {
-      error ("%<#pragma omp declare simd%> not immediately followed by "
-	     "a function declaration or definition");
+      error ("%<#pragma omp declare %s%> not immediately followed by "
+	     "a function declaration or definition", kind);
       clauses[0].type = CPP_EOF;
       return;
     }
   if (clauses.exists () && clauses[0].type != CPP_NAME)
     {
       error_at (DECL_SOURCE_LOCATION (fndecl),
-		"%<#pragma omp declare simd%> not immediately followed by "
-		"a single function declaration or definition");
+		"%<#pragma omp declare %s%> not immediately followed by "
+		"a single function declaration or definition", kind);
       clauses[0].type = CPP_EOF;
       return;
     }
@@ -19075,7 +19523,6 @@ c_finish_omp_declare_simd (c_parser *par
 
   unsigned int tokens_avail = parser->tokens_avail;
   gcc_assert (parser->tokens == &parser->tokens_buf[0]);
-  
 
   parser->tokens = clauses.address ();
   parser->tokens_avail = clauses.length ();
@@ -19085,19 +19532,27 @@ c_finish_omp_declare_simd (c_parser *par
     {
       c_token *token = c_parser_peek_token (parser);
       gcc_assert (token->type == CPP_NAME
-		  && strcmp (IDENTIFIER_POINTER (token->value), "simd") == 0);
+		  && strcmp (IDENTIFIER_POINTER (token->value), kind) == 0);
       c_parser_consume_token (parser);
       parser->in_pragma = true;
 
-      tree c = NULL_TREE;
-      c = c_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK,
-				      "#pragma omp declare simd");
-      c = c_omp_declare_simd_clauses_to_numbers (parms, c);
-      if (c != NULL_TREE)
-	c = tree_cons (NULL_TREE, c, NULL_TREE);
-      c = build_tree_list (get_identifier ("omp declare simd"), c);
-      TREE_CHAIN (c) = DECL_ATTRIBUTES (fndecl);
-      DECL_ATTRIBUTES (fndecl) = c;
+      if (strcmp (kind, "simd") == 0)
+	{
+	  tree c;
+	  c = c_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK,
+					"#pragma omp declare simd");
+	  c = c_omp_declare_simd_clauses_to_numbers (parms, c);
+	  if (c != NULL_TREE)
+	    c = tree_cons (NULL_TREE, c, NULL_TREE);
+	  c = build_tree_list (get_identifier ("omp declare simd"), c);
+	  TREE_CHAIN (c) = DECL_ATTRIBUTES (fndecl);
+	  DECL_ATTRIBUTES (fndecl) = c;
+	}
+      else
+	{
+	  gcc_assert (strcmp (kind, "variant") == 0);
+	  c_finish_omp_declare_variant (parser, fndecl, parms);
+	}
     }
 
   parser->tokens = &parser->tokens_buf[0];
@@ -19612,7 +20067,10 @@ c_parser_omp_declare_reduction (c_parser
    #pragma omp declare simd declare-simd-clauses[optseq] new-line
    #pragma omp declare reduction (reduction-id : typename-list : expression) \
       initializer-clause[opt] new-line
-   #pragma omp declare target new-line  */
+   #pragma omp declare target new-line
+
+   OpenMP 5.0
+   #pragma omp declare variant (identifier) match (context-selector)  */
 
 static void
 c_parser_omp_declare (c_parser *parser, enum pragma_context context)
@@ -19645,10 +20103,17 @@ c_parser_omp_declare (c_parser *parser,
 	  c_parser_omp_declare_target (parser);
 	  return;
 	}
+      if (strcmp (p, "variant") == 0)
+	{
+	  /* c_parser_consume_token (parser); done in
+	     c_parser_omp_declare_simd.  */
+	  c_parser_omp_declare_simd (parser, context);
+	  return;
+	}
     }
 
-  c_parser_error (parser, "expected %<simd%> or %<reduction%> "
-			  "or %<target%>");
+  c_parser_error (parser, "expected %<simd%>, %<reduction%>, "
+			  "%<target%> or %<variant%>");
   c_parser_skip_to_pragma_eol (parser);
 }
 
--- gcc/cp/parser.h.jj	2019-10-09 10:27:12.834398934 +0200
+++ gcc/cp/parser.h	2019-10-09 13:06:29.514475054 +0200
@@ -202,10 +202,11 @@ struct GTY (()) cp_parser_context {
 };
 
 
-/* Helper data structure for parsing #pragma omp declare simd.  */
+/* Helper data structure for parsing #pragma omp declare {simd,variant}.  */
 struct cp_omp_declare_simd_data {
   bool error_seen; /* Set if error has been reported.  */
   bool fndecl_seen; /* Set if one fn decl/definition has been seen already.  */
+  bool variant_p; /* Set for #pragma omp declare variant.  */
   vec<cp_token_cache_ptr> tokens;
   tree clauses;
 };
--- gcc/cp/parser.c.jj	2019-10-09 10:27:12.765399972 +0200
+++ gcc/cp/parser.c	2019-10-09 18:19:26.308418830 +0200
@@ -1375,8 +1375,9 @@ cp_ensure_no_omp_declare_simd (cp_parser
 {
   if (parser->omp_declare_simd && !parser->omp_declare_simd->error_seen)
     {
-      error ("%<#pragma omp declare simd%> not immediately followed by "
-	     "function declaration or definition");
+      error ("%<#pragma omp declare %s%> not immediately followed by "
+	     "function declaration or definition",
+	     parser->omp_declare_simd->variant_p ? "variant" : "simd");
       parser->omp_declare_simd = NULL;
     }
 }
@@ -35442,8 +35443,8 @@ cp_parser_oacc_clause_async (cp_parser *
 
 static tree
 cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
-			   const char *where, cp_token *pragma_tok,
-			   bool finish_p = true)
+			    const char *where, cp_token *pragma_tok,
+			    bool finish_p = true)
 {
   tree clauses = NULL;
   bool first = true;
@@ -35639,13 +35640,15 @@ cp_parser_oacc_all_clauses (cp_parser *p
 }
 
 /* Parse all OpenMP clauses.  The set clauses allowed by the directive
-   is a bitmask in MASK.  Return the list of clauses found; the result
-   of clause default goes in *pdefault.  */
+   is a bitmask in MASK.  Return the list of clauses found.
+   FINISH_P set if finish_omp_clauses should be called.
+   NESTED_P set if clauses should be terminated by closing paren instead
+   of end of pragma.  */
 
 static tree
 cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 			   const char *where, cp_token *pragma_tok,
-			   bool finish_p = true)
+			   bool finish_p = true, bool nested_p = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -35660,6 +35663,9 @@ cp_parser_omp_all_clauses (cp_parser *pa
       const char *c_name;
       tree prev = clauses;
 
+      if (nested_p && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	break;
+
       if (!first && cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
 	cp_lexer_consume_token (parser->lexer);
 
@@ -35979,7 +35985,8 @@ cp_parser_omp_all_clauses (cp_parser *pa
 	}
     }
  saw_error:
-  cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+  if (!nested_p)
+    cp_parser_skip_to_pragma_eol (parser, pragma_tok);
   if (finish_p)
     {
       if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_UNIFORM)) != 0)
@@ -39805,7 +39812,8 @@ cp_parser_oacc_wait (cp_parser *parser,
 
 static void
 cp_parser_omp_declare_simd (cp_parser *parser, cp_token *pragma_tok,
-			    enum pragma_context context)
+			    enum pragma_context context,
+			    bool variant_p)
 {
   bool first_p = parser->omp_declare_simd == NULL;
   cp_omp_declare_simd_data data;
@@ -39813,12 +39821,22 @@ cp_parser_omp_declare_simd (cp_parser *p
     {
       data.error_seen = false;
       data.fndecl_seen = false;
+      data.variant_p = variant_p;
       data.tokens = vNULL;
       data.clauses = NULL_TREE;
       /* It is safe to take the address of a local variable; it will only be
 	 used while this scope is live.  */
       parser->omp_declare_simd = &data;
     }
+  else if (parser->omp_declare_simd->variant_p != variant_p)
+    {
+      error_at (pragma_tok->location,
+		"%<#pragma omp declare %s%> followed by "
+		"%<#pragma omp declare %s%>",
+		parser->omp_declare_simd->variant_p ? "variant" : "simd",
+		parser->omp_declare_simd->variant_p ? "simd" : "variant");
+      parser->omp_declare_simd->error_seen = true;
+    }
 
   /* Store away all pragma tokens.  */
   while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)
@@ -39854,13 +39872,427 @@ cp_parser_omp_declare_simd (cp_parser *p
 	  && !parser->omp_declare_simd->error_seen
 	  && !parser->omp_declare_simd->fndecl_seen)
 	error_at (pragma_tok->location,
-		  "%<#pragma omp declare simd%> not immediately followed by "
-		  "function declaration or definition");
+		  "%<#pragma omp declare %s%> not immediately followed by "
+		  "function declaration or definition",
+		  parser->omp_declare_simd->variant_p ? "variant" : "simd");
       data.tokens.release ();
       parser->omp_declare_simd = NULL;
     }
 }
 
+static const char *const omp_construct_selectors[] = {
+  "simd", "target", "teams", "parallel", "for", NULL };
+static const char *const omp_device_selectors[] = {
+  "kind", "isa", "arch", NULL };
+static const char *const omp_implementation_selectors[] = {
+  "vendor", "extension", "atomic_default_mem_order", "unified_address",
+  "unified_shared_memory", "dynamic_allocators", "reverse_offload", NULL };
+static const char *const omp_user_selectors[] = {
+  "condition", NULL };
+
+/* OpenMP 5.0:
+
+   trait-selector:
+     trait-selector-name[([trait-score:]trait-property[,trait-property[,...]])]
+
+   trait-score:
+     score(score-expression)  */
+
+static tree
+cp_parser_omp_context_selector (cp_parser *parser, tree set, bool has_parms_p)
+{
+  tree ret = NULL_TREE;
+  do
+    {
+      tree selector;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_KEYWORD)
+	  || cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+	selector = cp_lexer_peek_token (parser->lexer)->u.value;
+      else
+	{
+	  cp_parser_error (parser, "expected trait selector name");
+	  return error_mark_node;
+	}
+
+      tree properties = NULL_TREE;
+      const char *const *selectors = NULL;
+      bool allow_score = true;
+      bool allow_user = false;
+      int property_limit = 0;
+      enum { CTX_PROPERTY_NONE, CTX_PROPERTY_USER, CTX_PROPERTY_IDLIST,
+	     CTX_PROPERTY_EXPR, CTX_PROPERTY_SIMD } property_kind
+	= CTX_PROPERTY_NONE;
+      switch (IDENTIFIER_POINTER (set)[0])
+	{
+	case 'c': /* construct */
+	  selectors = omp_construct_selectors;
+	  allow_score = false;
+	  property_limit = 1;
+	  property_kind = CTX_PROPERTY_SIMD;
+	  break;
+	case 'd': /* device */
+	  selectors = omp_device_selectors;
+	  allow_score = false;
+	  allow_user = true;
+	  property_limit = 3;
+	  property_kind = CTX_PROPERTY_IDLIST;
+	  break;
+	case 'i': /* implementation */
+	  selectors = omp_implementation_selectors;
+	  allow_user = true;
+	  property_limit = 3;
+	  property_kind = CTX_PROPERTY_IDLIST;
+	  break;
+	case 'u': /* user */
+	  selectors = omp_user_selectors;
+	  property_limit = 1;
+	  property_kind = CTX_PROPERTY_EXPR;
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      for (int i = 0; ; i++)
+	{
+	  if (selectors[i] == NULL)
+	    {
+	      if (allow_user)
+		{
+		  property_kind = CTX_PROPERTY_USER;
+		  break;
+		}
+	      else
+		{
+		  error ("selector %qs not allowed for context selector "
+			 "set %qs", IDENTIFIER_POINTER (selector),
+			 IDENTIFIER_POINTER (set));
+		  cp_lexer_consume_token (parser->lexer);
+		  return error_mark_node;
+		}
+	    }
+	  if (i == property_limit)
+	    property_kind = CTX_PROPERTY_NONE;
+	  if (strcmp (selectors[i], IDENTIFIER_POINTER (selector)) == 0)
+	    break;
+	}
+
+      cp_lexer_consume_token (parser->lexer);
+
+      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+	{
+	  if (property_kind == CTX_PROPERTY_NONE)
+	    {
+	      error ("selector %qs does not accept any properties",
+		     IDENTIFIER_POINTER (selector));
+	      return error_mark_node;
+	    }
+
+	  matching_parens parens;
+	  parens.consume_open (parser);
+
+	  cp_token *token = cp_lexer_peek_token (parser->lexer);
+	  if (allow_score
+	      && cp_lexer_next_token_is (parser->lexer, CPP_NAME)
+	      && strcmp (IDENTIFIER_POINTER (token->u.value), "score") == 0
+	      && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN))
+	    {
+	      cp_lexer_save_tokens (parser->lexer);
+	      cp_lexer_consume_token (parser->lexer);
+	      cp_lexer_consume_token (parser->lexer);
+	      if (cp_parser_skip_to_closing_parenthesis (parser, false, false,
+							 true)
+		  && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+		{
+		  cp_lexer_rollback_tokens (parser->lexer);
+		  cp_lexer_consume_token (parser->lexer);
+
+		  matching_parens parens2;
+		  parens2.require_open (parser);
+		  tree score = cp_parser_constant_expression (parser);
+		  if (!parens2.require_close (parser))
+		    cp_parser_skip_to_closing_parenthesis (parser, true,
+							   false, true);
+		  cp_parser_require (parser, CPP_COLON, RT_COLON);
+		  if (score != error_mark_node)
+		    {
+		      score = fold_non_dependent_expr (score);
+		      if (!value_dependent_expression_p (score)
+			  && (!INTEGRAL_TYPE_P (TREE_TYPE (score))
+			      || !tree_fits_shwi_p (score)))
+			error_at (token->location, "score argument must be "
+				  "constant integer expression");
+		      else
+			properties = tree_cons (get_identifier (" score"),
+						score, properties);
+		    }
+		}
+	      else
+		cp_lexer_rollback_tokens (parser->lexer);
+
+	      token = cp_lexer_peek_token (parser->lexer);
+	    }
+
+	  switch (property_kind)
+	    {
+	      tree t;
+	    case CTX_PROPERTY_USER:
+	      do
+		{
+		  t = cp_parser_constant_expression (parser);
+		  if (t != error_mark_node)
+		    {
+		      t = fold_non_dependent_expr (t);
+		      if (TREE_CODE (t) == STRING_CST)
+			properties = tree_cons (NULL_TREE, t, properties);
+		      else if (!value_dependent_expression_p (t)
+			       && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+				   || !tree_fits_shwi_p (t)))
+			error_at (token->location, "property must be "
+				  "constant integer expression or string "
+				  "literal");
+		      else
+			properties = tree_cons (NULL_TREE, t, properties);
+		    }
+
+		  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+		    cp_lexer_consume_token (parser->lexer);
+		  else
+		    break;
+		}
+	      while (1);
+	      break;
+	    case CTX_PROPERTY_IDLIST:
+	      do
+		{
+		  tree prop;
+		  if (cp_lexer_next_token_is (parser->lexer, CPP_KEYWORD)
+		      || cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+		    prop = cp_lexer_peek_token (parser->lexer)->u.value;
+		  else
+		    {
+		      cp_parser_error (parser, "expected identifier");
+		      return error_mark_node;
+		    }
+		  cp_lexer_consume_token (parser->lexer);
+
+		  properties = tree_cons (prop, NULL_TREE, properties);
+
+		  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+		    cp_lexer_consume_token (parser->lexer);
+		  else
+		    break;
+		}
+	      while (1);
+	      break;
+	    case CTX_PROPERTY_EXPR:
+	      t = cp_parser_constant_expression (parser);
+	      if (t != error_mark_node)
+		{
+		  t = fold_non_dependent_expr (t);
+		  if (!value_dependent_expression_p (t)
+		      && (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+			  || !tree_fits_shwi_p (t)))
+		    error_at (token->location, "property must be "
+			      "constant integer expression");
+		  else
+		    properties = tree_cons (NULL_TREE, t, properties);
+		}
+	      break;
+	    case CTX_PROPERTY_SIMD:
+	      if (!has_parms_p)
+		{
+		  error_at (token->location, "properties for %<simd%> "
+			    "selector may not be specified in "
+			    "%<metadirective%>");
+		  return error_mark_node;
+		}
+	      tree c;
+	      c = cp_parser_omp_all_clauses (parser,
+					     OMP_DECLARE_SIMD_CLAUSE_MASK,
+					     "simd", NULL, true, true);
+	      properties = tree_cons (NULL_TREE, c, properties);
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+
+	  if (!parens.require_close (parser))
+	    cp_parser_skip_to_closing_parenthesis (parser, true, false, true);
+
+	  properties = nreverse (properties);
+	}
+      else if (property_kind == CTX_PROPERTY_IDLIST
+	       || property_kind == CTX_PROPERTY_EXPR)
+	{
+	  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	  return error_mark_node;
+	}
+
+      ret = tree_cons (selector, properties, ret);
+
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	cp_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+  while (1);
+
+  return nreverse (ret);
+}
+
+/* OpenMP 5.0:
+
+   trait-set-selector[,trait-set-selector[,...]]
+
+   trait-set-selector:
+     trait-set-selector-name = { trait-selector[, trait-selector[, ...]] }
+
+   trait-set-selector-name:
+     constructor
+     device
+     implementation
+     user  */
+
+static tree
+cp_parser_omp_context_selector_specification (cp_parser *parser,
+					      bool has_parms_p)
+{
+  tree ret = NULL_TREE;
+  do
+    {
+      const char *setp = "";
+      if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+	setp
+	  = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value);
+      switch (setp[0])
+	{
+	case 'c':
+	  if (strcmp (setp, "construct") == 0)
+	    setp = NULL;
+	  break;
+	case 'd':
+	  if (strcmp (setp, "device") == 0)
+	    setp = NULL;
+	  break;
+	case 'i':
+	  if (strcmp (setp, "implementation") == 0)
+	    setp = NULL;
+	  break;
+	case 'u':
+	  if (strcmp (setp, "user") == 0)
+	    setp = NULL;
+	  break;
+	default:
+	  break;
+	}
+      if (setp)
+	{
+	  cp_parser_error (parser, "expected %<construct%>, %<device%>, "
+				   "%<implementation%> or %<user%>");
+	  return error_mark_node;
+	}
+
+      tree set = cp_lexer_peek_token (parser->lexer)->u.value;
+      cp_lexer_consume_token (parser->lexer);
+
+      if (!cp_parser_require (parser, CPP_EQ, RT_EQ))
+	return error_mark_node;
+
+      matching_braces braces;
+      if (!braces.require_open (parser))
+	return error_mark_node;
+
+      tree selectors
+	= cp_parser_omp_context_selector (parser, set, has_parms_p);
+      if (selectors == error_mark_node)
+	{
+	  cp_parser_skip_to_closing_brace (parser);
+	  ret = error_mark_node;
+	}
+      else if (ret != error_mark_node)
+	ret = tree_cons (set, selectors, ret);
+
+      braces.require_close (parser);
+
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	cp_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+  while (1);
+
+  if (ret == error_mark_node)
+    return ret;
+  return nreverse (ret);
+}
+
+/* Finalize #pragma omp declare variant after a fndecl has been parsed, and put
+   that into "omp declare variant" attribute.  */
+
+static tree
+cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
+			       tree attrs)
+{
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    {
+     fail:
+      cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+      return attrs;
+    }
+
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  tree variant;
+  tree name = cp_parser_id_expression (parser, /*template_p=*/false,
+				       /*check_dependency_p=*/true,
+				       /*template_p=*/NULL,
+				       /*declarator_p=*/false,
+				       /*optional_p=*/false);
+  if (identifier_p (name))
+    variant = cp_parser_lookup_name_simple (parser, name, token->location);
+  else
+    variant = name;
+  if (variant == error_mark_node)
+    {
+      cp_parser_name_lookup_error (parser, name, variant, NLE_NULL,
+				   token->location);
+      variant = error_mark_node;
+    }
+
+  parens.require_close (parser);
+
+  const char *clause = "";
+  location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
+  if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+    clause = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value);
+  if (strcmp (clause, "match"))
+    {
+      cp_parser_error (parser, "expected %<match%>");
+      goto fail;
+    }
+
+  cp_lexer_consume_token (parser->lexer);
+
+  if (!parens.require_open (parser))
+    goto fail;
+
+  tree ctx = cp_parser_omp_context_selector_specification (parser, true);
+  if (ctx == error_mark_node)
+    goto fail;
+  ctx = c_omp_check_context_selector (match_loc, ctx);
+  if (ctx != error_mark_node && variant != error_mark_node)
+    {
+      attrs = tree_cons (get_identifier ("omp declare variant"),
+			 build_tree_list (variant, ctx), attrs);
+      if (processing_template_decl)
+	ATTR_IS_DEPENDENT (attrs) = 1;
+    }
+
+  parens.require_close (parser);
+  cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+  return attrs;
+}
+
+
 /* Finalize #pragma omp declare simd clauses after direct declarator has
    been parsed, and put that into "omp declare simd" attribute.  */
 
@@ -39873,8 +40305,9 @@ cp_parser_late_parsing_omp_declare_simd
 
   if (!data->error_seen && data->fndecl_seen)
     {
-      error ("%<#pragma omp declare simd%> not immediately followed by "
-	     "a single function declaration or definition");
+      error ("%<#pragma omp declare %s%> not immediately followed by "
+	     "a single function declaration or definition",
+	     data->variant_p ? "variant" : "simd");
       data->error_seen = true;
     }
   if (data->error_seen)
@@ -39888,17 +40321,28 @@ cp_parser_late_parsing_omp_declare_simd
       parser->lexer->in_pragma = true;
       gcc_assert (cp_lexer_peek_token (parser->lexer)->type == CPP_PRAGMA);
       cp_token *pragma_tok = cp_lexer_consume_token (parser->lexer);
+      tree id = cp_lexer_peek_token (parser->lexer)->u.value;
+      const char *kind = IDENTIFIER_POINTER (id);
       cp_lexer_consume_token (parser->lexer);
-      cl = cp_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK,
-				      "#pragma omp declare simd", pragma_tok);
+      if (strcmp (kind, "simd") == 0)
+	{
+	  cl = cp_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK,
+					  "#pragma omp declare simd",
+					  pragma_tok);
+	  if (cl)
+	    cl = tree_cons (NULL_TREE, cl, NULL_TREE);
+	  c = build_tree_list (get_identifier ("omp declare simd"), cl);
+	  TREE_CHAIN (c) = attrs;
+	  if (processing_template_decl)
+	    ATTR_IS_DEPENDENT (c) = 1;
+	  attrs = c;
+	}
+      else
+	{
+	  gcc_assert (strcmp (kind, "variant") == 0);
+	  attrs = cp_finish_omp_declare_variant (parser, pragma_tok, attrs);
+	}
       cp_parser_pop_lexer (parser);
-      if (cl)
-	cl = tree_cons (NULL_TREE, cl, NULL_TREE);
-      c = build_tree_list (get_identifier ("omp declare simd"), cl);
-      TREE_CHAIN (c) = attrs;
-      if (processing_template_decl)
-	ATTR_IS_DEPENDENT (c) = 1;
-      attrs = c;
     }
 
   data->fndecl_seen = true;
@@ -40462,7 +40906,10 @@ cp_parser_omp_declare_reduction (cp_pars
    #pragma omp declare simd declare-simd-clauses[optseq] new-line
    #pragma omp declare reduction (reduction-id : typename-list : expression) \
       initializer-clause[opt] new-line
-   #pragma omp declare target new-line  */
+   #pragma omp declare target new-line
+
+   OpenMP 5.0
+   #pragma omp declare variant (identifier) match (context-selector)  */
 
 static bool
 cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok,
@@ -40477,7 +40924,14 @@ cp_parser_omp_declare (cp_parser *parser
 	{
 	  cp_lexer_consume_token (parser->lexer);
 	  cp_parser_omp_declare_simd (parser, pragma_tok,
-				      context);
+				      context, false);
+	  return true;
+	}
+      if (flag_openmp && strcmp (p, "variant") == 0)
+	{
+	  cp_lexer_consume_token (parser->lexer);
+	  cp_parser_omp_declare_simd (parser, pragma_tok,
+				      context, true);
 	  return true;
 	}
       cp_ensure_no_omp_declare_simd (parser);
@@ -40500,8 +40954,8 @@ cp_parser_omp_declare (cp_parser *parser
 	  return false;
 	}
     }
-  cp_parser_error (parser, "expected %<simd%> or %<reduction%> "
-			   "or %<target%>");
+  cp_parser_error (parser, "expected %<simd%>, %<reduction%>, "
+			   "%<target%> or %<variant%>");
   cp_parser_require_pragma_eol (parser, pragma_tok);
   return false;
 }
--- gcc/testsuite/c-c++-common/gomp/declare-variant-1.c.jj	2019-10-09 13:06:29.514475054 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-1.c	2019-10-09 18:19:26.310418800 +0200
@@ -0,0 +1,54 @@
+int foo (int, int, int *);
+int bar (int, int, int *);
+#pragma omp declare variant (foo) \
+  match (construct={parallel,for},\
+	 device={isa(avx512f,avx512vl),kind(host,cpu)},\
+	 implementation={vendor(score(0):gnu),unified_shared_memory},\
+	 user={condition(score(0):0)})
+#pragma omp declare variant (bar) \
+  match (device={arch(x86_64,powerpc64),isa(avx512f,popcntb)}, \
+	 implementation={atomic_default_mem_order(seq_cst),made_up_selector("foo", 13, "bar")}, \
+	 user={condition(3-3)})
+int baz (int, int, int *);
+
+int
+qux (void)
+{
+  int i = 3;
+  return baz (1, 2, &i);
+}
+
+int quux (int);
+
+void
+corge (void)
+{
+  int i;
+  #pragma omp declare variant (quux) match (construct={parallel,for})
+  extern int waldo (int);
+  waldo (5);
+  #pragma omp parallel for
+  for (i = 0; i < 3; i++)
+    waldo (6);
+  #pragma omp parallel
+  #pragma omp taskgroup
+  #pragma omp for
+  for (i = 0; i < 3; i++)
+    waldo (7);
+  #pragma omp parallel
+  #pragma omp master    
+  waldo (8);
+}
+
+#pragma omp declare variant (bar) match \
+  (implementation={atomic_default_mem_order(relaxed), \
+		   unified_address, unified_shared_memory, \
+		   dynamic_allocators, reverse_offload})
+int baz2 (int x, int y, int *z)
+{
+  return x + y + *z;
+}
+
+#pragma omp declare variant (bar) match \
+  (implementation={atomic_default_mem_order(score(3): acq_rel)})
+int baz3 (int, int, int *);
--- gcc/testsuite/c-c++-common/gomp/declare-variant-2.c.jj	2019-10-09 13:06:29.514475054 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-2.c	2019-10-09 18:11:48.045314257 +0200
@@ -0,0 +1,122 @@
+void f1 (void);
+#pragma omp declare variant	/* { dg-error "expected '\\(' before end of line" } */
+void f2 (void);
+#pragma omp declare variant (	/* { dg-error "" } */
+void f3 (void);
+#pragma omp declare variant ()	/* { dg-error "" } */
+void f4 (void);
+#pragma omp declare variant match(user={condition(0)})	/* { dg-error "expected '\\(' before 'match'" } */
+void f5 (void);
+#pragma omp declare variant (f1)	/* { dg-error "expected 'match' before end of line" } */
+void f6 (void);
+#pragma omp declare variant (f1) simd	/* { dg-error "expected 'match' before 'simd'" } */
+void f7 (void);
+#pragma omp declare variant (f1) match	/* { dg-error "expected '\\(' before end of line" } */
+void f8 (void);
+#pragma omp declare variant (f1) match(	/* { dg-error "expected 'construct', 'device', 'implementation' or 'user' before end of line" } */
+void f9 (void);
+#pragma omp declare variant (f1) match()	/* { dg-error "expected 'construct', 'device', 'implementation' or 'user' before '\\)' token" } */
+void f10 (void);
+#pragma omp declare variant (f1) match(foo)	/* { dg-error "expected 'construct', 'device', 'implementation' or 'user' before 'foo'" } */
+void f11 (void);
+#pragma omp declare variant (f1) match(something={something})	/* { dg-error "expected 'construct', 'device', 'implementation' or 'user' before 'something'" } */
+void f12 (void);
+#pragma omp declare variant (f1) match(user)	/* { dg-error "expected '=' before '\\)' token" } */
+void f13 (void);
+#pragma omp declare variant (f1) match(user=)	/* { dg-error "expected '\\\{' before '\\)' token" } */
+void f14 (void);
+#pragma omp declare variant (f1) match(user=	/* { dg-error "expected '\\\{' before end of line" } */
+void f15 (void);
+#pragma omp declare variant (f1) match(user={)	/* { dg-error "expected trait selector name before '\\)' token" } */
+void f16 (void);				/* { dg-error "expected '\\\}' before" "" { target c++ } .-1 } */
+#pragma omp declare variant (f1) match(user={})	/* { dg-error "expected trait selector name before '\\\}' token" } */
+void f17 (void);
+#pragma omp declare variant (f1) match(user={condition})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f18 (void);
+#pragma omp declare variant (f1) match(user={condition(})	/* { dg-error "expected \[^\n\r]*expression before '\\\}' token" } */
+void f19 (void);						/* { dg-error "expected '\\)' before '\\\}' token" "" { target c++ } .-1 } */
+#pragma omp declare variant (f1) match(user={condition()})	/* { dg-error "expected \[^\n\r]*expression before '\\)' token" } */
+void f20 (void);
+#pragma omp declare variant (f1) match(user={condition(f1)})	/* { dg-error "property must be constant integer expression" "" { target { c || c++11 } } } */
+void f21 (void);						/* { dg-error "cannot appear in a constant-expression" "" { target c++98_only } .-1 } */
+#pragma omp declare variant (f1) match(user={condition(1, 2, 3)})	/* { dg-error "expected '\\)' before ',' token" } */
+void f22 (void);
+#pragma omp declare variant (f1) match(construct={master})	/* { dg-error "selector 'master' not allowed for context selector set 'construct'" } */
+void f23 (void);
+#pragma omp declare variant (f1) match(construct={teams,parallel,master,for})	/* { dg-error "selector 'master' not allowed for context selector set 'construct'" } */
+void f24 (void);						/* { dg-error "expected '\\\}' before ',' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(construct={parallel(1	/* { dg-error "selector 'parallel' does not accept any properties" } */
+void f25 (void);						/* { dg-error "expected '\\\}' before end of line" "" { target c++ } .-1 } */
+								/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-2 } */
+#pragma omp declare variant (f1) match(construct={parallel(1)})	/* { dg-error "selector 'parallel' does not accept any properties" } */
+void f26 (void);							/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(construct={simd(12)})	/* { dg-error "expected \[^\n\r]* clause before" } */
+void f27 (void);						/* { dg-error "'\\)' before numeric constant" "" { target c++ } .-1 } */
+#pragma omp declare variant (f1) match(construct={parallel},construct={for})	/* { dg-error "selector set 'construct' specified more than once" } */
+void f28 (void);
+#pragma omp declare variant (f1) match(construct={parallel},construct={parallel})	/* { dg-error "selector set 'construct' specified more than once" } */
+void f29 (void);
+#pragma omp declare variant (f1) match(user={condition(0)},construct={target},user={condition(0)})	/* { dg-error "selector set 'user' specified more than once" } */
+void f30 (void);
+#pragma omp declare variant (f1) match(user={condition(0)},user={condition(1)})	/* { dg-error "selector set 'user' specified more than once" } */
+void f31 (void);
+#pragma omp declare variant (f1) match(device={kind})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f32 (void);
+#pragma omp declare variant (f1) match(device={isa})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f33 (void);
+#pragma omp declare variant (f1) match(device={arch})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f34 (void);
+#pragma omp declare variant (f1) match(device={kind,isa,arch})	/* { dg-error "expected '\\(' before ',' token" } */
+void f35 (void);
+#pragma omp declare variant (f1) match(device={kind(})	/* { dg-error "expected identifier before '\\\}' token" } */
+void f36 (void);
+#pragma omp declare variant (f1) match(device={kind(unknown)})	/* { dg-warning "unknown property 'unknown' of 'kind' selector" } */
+void f37 (void);
+#pragma omp declare variant (f1) match(device={kind(unknown,foobar)})	/* { dg-warning "unknown property 'unknown' of 'kind' selector" } */
+void f38 (void);							/* { dg-warning "unknown property 'foobar' of 'kind' selector" "" { target *-*-* } .-1 } */
+#pragma omp declare variant (f1) match(device={isa(1)})	/* { dg-error "expected identifier before numeric constant" } */
+void f39 (void);
+#pragma omp declare variant (f1) match(device={arch(17)})	/* { dg-error "expected identifier before numeric constant" } */
+void f40 (void);
+#pragma omp declare variant (f1) match(device={foobar(3)})
+void f41 (void);
+#pragma omp declare variant (f1) match(device={arch(x86_64)},device={isa(avx512vl)})	/* { dg-error "selector set 'device' specified more than once" } */
+void f42 (void);
+#pragma omp declare variant (f1) match(implementation={foobar(3)})
+void f43 (void);
+#pragma omp declare variant (f1) match(implementation={vendor})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f44 (void);
+#pragma omp declare variant (f1) match(implementation={extension})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f45 (void);
+#pragma omp declare variant (f1) match(implementation={vendor()})	/* { dg-error "expected identifier before '\\)' token" } */
+void f45 (void);
+#pragma omp declare variant (f1) match(implementation={vendor(123-234)})	/* { dg-error "expected identifier before numeric constant" } */
+void f46 (void);
+#pragma omp declare variant (f1) match(implementation={vendor("x86_64")})	/* { dg-error "expected identifier before string constant" } */
+void f47 (void);
+#pragma omp declare variant (f1) match(implementation={unified_address(yes)})	/* { dg-error "selector 'unified_address' does not accept any properties" } */
+void f48 (void);								/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(implementation={unified_shared_memory(no)})	/* { dg-error "selector 'unified_shared_memory' does not accept any properties" } */
+void f49 (void);									/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(implementation={dynamic_allocators(42)})	/* { dg-error "selector 'dynamic_allocators' does not accept any properties" } */
+void f50 (void);								/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(implementation={reverse_offload()})	/* { dg-error "selector 'reverse_offload' does not accept any properties" } */
+void f51 (void);								/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(implementation={atomic_default_mem_order})	/* { dg-error "expected '\\(' before '\\\}' token" } */
+void f52 (void);
+#pragma omp declare variant (f1) match(implementation={atomic_default_mem_order(acquire)})	/* { dg-error "incorrect property 'acquire' of 'atomic_default_mem_order' selector" } */
+void f53 (void);
+#pragma omp declare variant (f1) match(implementation={atomic_default_mem_order(release)})	/* { dg-error "incorrect property 'release' of 'atomic_default_mem_order' selector" } */
+void f54 (void);
+#pragma omp declare variant (f1) match(implementation={atomic_default_mem_order(foobar)})	/* { dg-error "incorrect property 'foobar' of 'atomic_default_mem_order' selector" } */
+void f55 (void);
+#pragma omp declare variant (f1) match(implementation={atomic_default_mem_order(relaxed,seq_cst)})	/* { dg-error "'atomic_default_mem_order' selector must have a single property" } */
+void f56 (void);
+#pragma omp declare variant (f1) match(implementation={atomic_default_mem_order(relaxed)},implementation={atomic_default_mem_order(relaxed)})	/* { dg-error "selector set 'implementation' specified more than once" } */
+void f57 (void);
+#pragma omp declare variant (f1) match(user={foobar(3)})	/* { dg-error "selector 'foobar' not allowed for context selector set 'user'" } */
+void f58 (void);						/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(construct={foobar(3)})	/* { dg-error "selector 'foobar' not allowed for context selector set 'construct'" } */
+void f59 (void);						/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
+#pragma omp declare variant (f1) match(construct={parallel},foobar={bar})	/* { dg-error "expected 'construct', 'device', 'implementation' or 'user' before 'foobar'" } */
+void f60 (void);
--- gcc/testsuite/c-c++-common/gomp/declare-variant-3.c.jj	2019-10-09 16:31:23.137883221 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-3.c	2019-10-09 16:57:12.457595048 +0200
@@ -0,0 +1,141 @@
+void f1 (void);
+#pragma omp declare variant (f1) match (construct={target})
+void f2 (void);
+void f3 (void);
+#pragma omp declare variant (f3) match (construct={teams})
+void f4 (void);
+void f5 (void);
+#pragma omp declare variant (f5) match (construct={parallel})
+void f6 (void);
+void f7 (void);
+#pragma omp declare variant (f7) match (construct={for})
+void f8 (void);
+void f9 (void);
+#pragma omp declare variant (f9) match (construct={target,teams,parallel,for})
+void f10 (void);
+void f11 (void);
+#pragma omp declare variant (f11) match (construct={teams,for,parallel})
+void f12 (void);
+void f13 (void);
+#pragma omp declare variant (f13) match (device={kind(any)})
+void f14 (void);
+#pragma omp declare variant (f13) match (device={kind(host)})
+void f15 (void);
+#pragma omp declare variant (f13) match (device={kind(nohost)})
+void f16 (void);
+#pragma omp declare variant (f13) match (device={kind(cpu)})
+void f17 (void);
+#pragma omp declare variant (f13) match (device={kind(gpu)})
+void f18 (void);
+#pragma omp declare variant (f13) match (device={kind(fpga)})
+void f19 (void);
+#pragma omp declare variant (f13) match (device={kind(any,any)})
+void f20 (void);
+#pragma omp declare variant (f13) match (device={kind(host,nohost)})
+void f21 (void);
+#pragma omp declare variant (f13) match (device={kind(cpu,gpu,fpga)})
+void f22 (void);
+#pragma omp declare variant (f13) match (device={kind(any,cpu,nohost)})
+void f23 (void);
+#pragma omp declare variant (f13) match (device={isa(avx)})
+void f24 (void);
+#pragma omp declare variant (f13) match (device={isa(sse4,avx512f,avx512vl,avx512bw)})
+void f25 (void);
+#pragma omp declare variant (f13) match (device={arch(x86_64)})
+void f26 (void);
+#pragma omp declare variant (f13) match (device={arch(riscv64)})
+void f27 (void);
+#pragma omp declare variant (f13) match (device={arch(nvptx)})
+void f28 (void);
+#pragma omp declare variant (f13) match (device={arch(x86_64),isa(avx512f,avx512vl),kind(cpu)})
+void f29 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(amd)})
+void f30 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(arm)})
+void f31 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(bsc)})
+void f32 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(cray)})
+void f33 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(fujitsu)})
+void f34 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(gnu)})
+void f35 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(ibm)})
+void f36 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(intel)})
+void f37 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(llvm)})
+void f38 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(pgi)})
+void f39 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(ti)})
+void f40 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(unknown)})
+void f41 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(gnu,llvm,intel,ibm)})
+void f42 (void);
+#pragma omp declare variant (f13) match (implementation={extension(my_cute_extension)})	/* { dg-warning "unknown property 'my_cute_extension' of 'extension' selector" } */
+void f43 (void);
+#pragma omp declare variant (f13) match (implementation={extension(some_other_ext,another_ext)})	/* { dg-warning "unknown property 'some_other_ext' of 'extension' selector" } */
+void f44 (void);											/* { dg-warning "unknown property 'another_ext' of 'extension' selector" "" { target *-*-* } .-1 } */
+#pragma omp declare variant (f13) match (implementation={unified_shared_memory})
+void f45 (void);
+#pragma omp declare variant (f13) match (implementation={unified_address})
+void f46 (void);
+#pragma omp declare variant (f13) match (implementation={dynamic_allocators})
+void f47 (void);
+#pragma omp declare variant (f13) match (implementation={reverse_offload})
+void f48 (void);
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(seq_cst)})
+void f49 (void);
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(relaxed)})
+void f50 (void);
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(acq_rel)})
+void f51 (void);
+#pragma omp declare variant (f14) match (implementation={atomic_default_mem_order(acq_rel),vendor(gnu),unified_address,extension(foobar)})	/* { dg-warning "unknown property 'foobar' of 'extension' selector" } */
+void f52 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(3):amd)})
+void f53 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(4):arm)})
+void f54 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(5):bsc)})
+void f55 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(6):cray)})
+void f56 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(7):fujitsu)})
+void f57 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(8):gnu)})
+void f58 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(9):ibm)})
+void f59 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(10):intel)})
+void f60 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(11):llvm)})
+void f61 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(12):pgi)})
+void f62 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(13):ti)})
+void f63 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(14):unknown)})
+void f64 (void);
+#pragma omp declare variant (f13) match (implementation={vendor(score(15):gnu,llvm,intel,ibm)})
+void f65 (void);
+#pragma omp declare variant (f13) match (implementation={extension(score(16):my_cute_extension)})	/* { dg-warning "unknown property 'my_cute_extension' of 'extension' selector" } */
+void f66 (void);
+#pragma omp declare variant (f13) match (implementation={extension(score(17):some_other_ext,another_ext)})	/* { dg-warning "unknown property 'some_other_ext' of 'extension' selector" } */
+void f67 (void);												/* { dg-warning "unknown property 'another_ext' of 'extension' selector" "" { target *-*-* } .-1 } */
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(score(18):seq_cst)})
+void f68 (void);
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(score(19):relaxed)})
+void f69 (void);
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(score(20):acq_rel)})
+void f70 (void);
+#pragma omp declare variant (f13) match (implementation={atomic_default_mem_order(score(21):acq_rel),vendor(score(22):gnu),unified_address,extension(score(22):foobar)})	/* { dg-warning "unknown property 'foobar' of 'extension' selector" } */
+void f71 (void);
+#pragma omp declare variant (f13) match (user={condition(0)})
+void f72 (void);
+#pragma omp declare variant (f13) match (user={condition(272-272*1)})
+void f73 (void);
+#pragma omp declare variant (f13) match (user={condition(score(25):1)})
+void f74 (void);
--- gcc/testsuite/g++.dg/gomp/this-1.C.jj	2019-10-09 10:27:11.134424496 +0200
+++ gcc/testsuite/g++.dg/gomp/this-1.C	2019-10-09 13:06:29.514475054 +0200
@@ -3,7 +3,7 @@
 
 struct S
 {
-  #pragma omp declare simd linear(this)		// { dg-error "is not an function argument" }
+  #pragma omp declare simd linear(this)		// { dg-error "is not a function argument" }
   static void foo ();
   void bar ();
 };
@@ -35,7 +35,7 @@ S::bar ()
 template <int N>
 struct T
 {
-  #pragma omp declare simd linear(this)		// { dg-error "is not an function argument" }
+  #pragma omp declare simd linear(this)		// { dg-error "is not a function argument" }
   static void foo ();
   void bar ();
 };
--- gcc/testsuite/gcc.dg/gomp/declare-variant-1.c.jj	2019-10-09 13:06:29.514475054 +0200
+++ gcc/testsuite/gcc.dg/gomp/declare-variant-1.c	2019-10-09 13:06:29.514475054 +0200
@@ -0,0 +1,41 @@
+/* Test parsing of #pragma omp declare variant */
+/* { dg-do compile } */
+
+int fn0 (int);
+int fn6 (int);
+
+#pragma omp declare variant (fn0) match (user={condition(0)})
+int a;	/* { dg-error "not immediately followed by a function declaration or definition" } */
+
+#pragma omp declare variant (fn0) match (user={condition(0)})
+int fn1 (int a), fn2 (int a);	/* { dg-error "not immediately followed by a single function declaration or definition" } */
+
+#pragma omp declare variant (fn0) match (user={condition(0)})
+int b, fn3 (int a);	/* { dg-error "not immediately followed by a function declaration or definition" } */
+
+#pragma omp declare variant (fn0) match (user={condition(0)})
+int fn4 (int a), c;	/* { dg-error "not immediately followed by a function declaration or definition" } */
+
+int t;
+
+#pragma omp declare variant (fn0) match (user={condition(0)})
+#pragma omp declare variant (fn6) match (implementation={vendor(unknown)})
+#pragma omp threadprivate(t)	/* { dg-error "must be followed by function declaration or definition or another" } */
+int fn5 (int a);
+
+#pragma omp declare variant (1 + 2) match (user={condition(0)})	/* { dg-error "expected identifier before numeric constant" } */
+int fn7 (int);
+
+#pragma omp declare variant (t) match (user={condition(0)})	/* { dg-error "variant 't' is not a function" } */
+int fn8 (int);
+
+long fn9 (char, short);
+
+#pragma omp declare variant (fn9) match (implementation={vendor(unknown)})	/* { dg-error "variant 'fn9' and base 'fn10' have incompatible types" } */
+int fn10 (int, long long);
+
+#pragma omp declare variant (memcpy) match (implementation={vendor(llvm)})	/* { dg-error "'memcpy' undeclared here" } */
+void *fn11 (void *, const void *, __SIZE_TYPE__);
+
+#pragma omp declare variant (__builtin_memmove) match (implementation={vendor(gnu)})	/* { dg-error "variant '__builtin_memmove' is a built-in" } */
+void *fn12 (void *, const void *, __SIZE_TYPE__);
--- gcc/testsuite/gcc.dg/gomp/declare-variant-2.c.jj	2019-10-09 13:06:29.514475054 +0200
+++ gcc/testsuite/gcc.dg/gomp/declare-variant-2.c	2019-10-09 13:06:29.514475054 +0200
@@ -0,0 +1,22 @@
+/* Test parsing of #pragma omp declare variant */
+/* { dg-do compile } */
+
+int f0 (int, int *, int);
+
+int
+f1 (int x)
+{
+  if (x)
+    #pragma omp declare variant (fn0) match (user={condition(0)})
+    extern int f3 (int a, int *b, int c);	/* { dg-error "must be followed by function declaration or definition" } */
+  while (x < 10)
+    #pragma omp declare variant (fn0) match (user={condition(0)})
+    extern int f4 (int a, int *b, int c);	/* { dg-error "must be followed by function declaration or definition" } */
+  {
+lab:
+    #pragma omp declare variant (fn0) match (user={condition(0)})
+    extern int f5 (int a, int *b, int c);	/* { dg-error "must be followed by function declaration or definition" } */
+    x++;					/* { dg-error "expected expression before" "" { target *-*-* } .-1 } */
+  }
+  return x;
+}
