This rather contained OpenMP patch does:

* 'interop' clause - some fixes (-Wunused),

* ICE fix related to omp_interop_t type check.

* Handle 'append_args' in C/C++ (depends on recently added
  'dispatch' and utilizes the existing 'init' clause parser
  of 'omp interop').

* Update gimplify for append_args - for a special case, actual
  code gen happens, replacing a sorry; it also prepares for the
  fully support. Currently, it requires dispatch with 'interop'
  clause (otherwise: libgomp call required) and the 'device'
  clause (requires call to existing routine; still to decide how
  to add a new 'enum' value: hidden, as ompx_gnu_ (+ name?) or
  as new 6.1 official enum? - otherwise: trivial).

* Improve clause error diagnostic with declare_variant.

* Cleanup of 'dispatch' handling in gimplify_call_expr

Thus, in total: less 'sorry', prep for real code, better diagnostic
(for existing code and being less patchy), and one ICE fix.

Comments, remarks, suggestions?

Tobias

PS: For C, to get full interop support, two middle-end sorry calls have to be
replaced by simple libgomp calls (the one in here and the one for 'interop'
itself). For C++, some *_device_addr fixes are required, once I understand how
it is best done (standard support vs. legacy case handling); for Fortran, some
smaller parser additions are required, once 'omp dispatch' is in (revised patch
expected this week).

PPS: OpenMP 5.0/5.1 support is rather complete (except for OMPT/OMPD). For 5.0,
mainly 'metadirectives' is required, but also the second part of Fortran deep
mapping of allocatable components and declare mapper. (All have posted patches.)
For 5.1, 'begin/end declare_variant' and some mapping updates (strided update,
iterator) are missing (patches exist), plus remaining bits of interop and a few
minor issues (features, corner cases and bugs).
OpenMP: Add declare variant's 'append_args' clause in C/C++

Add the append_args clause of 'declare variant' to C and C++,
fix/improve diagnostic for 'interop' clause and 'declare_variant'
clauses on the way.

Cleanup dispatch handling in gimplify_call_expr a bit and
partially handle 'append_args'. (Namely, those parts that
do not require libraries calls, i.e. a dispatch construct
where the 'device' and 'interop' clause has been specified.)

The sorry can be removed once an enum value like
  omp_ipr_(ompx_gnu_)omp_device_num (cf. OpenMP Spec Issue 4451)
has to be added to the runtime side such that omp_get_interop_int
returns the device number of an interop object (as passed to
dispatch via the interop clause); and a call to GOMP_interop
has to be added to create interop objects. Once available, only
a very localized change in gimplify_call_expr is required to
claim for full support. - And Fortran parsing support.

gcc/c-family/ChangeLog:

	* c-omp.cc (c_omp_interop_t_p): Handle error_mark_node.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_omp_clause_init_modifiers): New;
	split of from ...
	(c_parser_omp_clause_init): ... here; call it.
	(c_finish_omp_declare_variant): Parse 'append_args' clause.
	(c_parser_omp_clause_interop): Set tree used/read.

gcc/cp/ChangeLog:

	* decl.cc (omp_declare_variant_finalize_one): Handle
	append_args.
	* parser.cc (cp_parser_omp_clause_init_modifiers): New;
	split of from ...
	(cp_parser_omp_clause_init):  ... here; call it.
	(cp_parser_omp_all_clauses): Replace interop parsing by
	a call to ...
	(cp_parser_omp_clause_interop): ... this new function;
	set tree used/read.
	(cp_finish_omp_declare_variant): Parse 'append_args' clause.
	(cp_parser_omp_declare): Update comment.
	* pt.cc (tsubst_attribute, tsubst_omp_clauses): Handle template
	substitution also for declare variant's append_args clause,
	using for 'init' the same code as for interop's init clause.

gcc/ChangeLog:

	* gimplify.cc (gimplify_call_expr): Update for OpenMP's
	append_args; cleanup of OpenMP's dispatch clause handling.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/declare-variant-2.c: Update dg-error msg.
	* c-c++-common/gomp/dispatch-12.c: Likewise.
	* c-c++-common/gomp/dispatch-11.c: Likewise and extend a bit.
	* c-c++-common/gomp/append-args-1.c: New test.
	* c-c++-common/gomp/append-args-2.c: New test.
	* c-c++-common/gomp/append-args-3.c: New test.
	* g++.dg/gomp/append-args-1.C: New test.
	* g++.dg/gomp/append-args-2.C: New test.
	* g++.dg/gomp/append-args-3.C: New test.

 gcc/c-family/c-omp.cc                              |   2 +
 gcc/c/c-parser.cc                                  | 397 +++++++++++++++------
 gcc/cp/decl.cc                                     |  86 ++++-
 gcc/cp/parser.cc                                   | 287 ++++++++++-----
 gcc/cp/pt.cc                                       |  11 +-
 gcc/gimplify.cc                                    | 162 +++++++--
 gcc/testsuite/c-c++-common/gomp/append-args-1.c    |  85 +++++
 gcc/testsuite/c-c++-common/gomp/append-args-2.c    |  10 +
 gcc/testsuite/c-c++-common/gomp/append-args-3.c    |  57 +++
 .../c-c++-common/gomp/declare-variant-2.c          |   4 +-
 gcc/testsuite/c-c++-common/gomp/dispatch-11.c      |  58 ++-
 gcc/testsuite/c-c++-common/gomp/dispatch-12.c      |  20 +-
 gcc/testsuite/g++.dg/gomp/append-args-1.C          | 136 +++++++
 gcc/testsuite/g++.dg/gomp/append-args-2.C          |  55 +++
 gcc/testsuite/g++.dg/gomp/append-args-3.C          | 100 ++++++
 15 files changed, 1205 insertions(+), 265 deletions(-)

diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 7e20e5a5082..5f2db146fed 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -669,6 +669,8 @@ c_finish_omp_atomic (location_t loc, enum tree_code code,
 bool
 c_omp_interop_t_p (tree type)
 {
+  if (type == error_mark_node)
+    return false;
   type = TYPE_MAIN_VARIANT (type);
   return (TREE_CODE (type) == ENUMERAL_TYPE
 	  && TYPE_NAME (type)
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index d0235809fb3..42fa383c57f 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -20630,6 +20630,73 @@ c_parser_omp_modifier_prefer_type (c_parser *parser)
   return res;
 }
 
+/* OpenMP 5.1
+     modifiers of the 'init' clause, used by the 'init' and the
+     'append_args' clauses.
+
+   Modifiers:
+     target
+     targetsync
+     prefer_type (preference-specification)
+
+  Returns 'false' if an error has been issued.  */
+
+static bool
+c_parser_omp_clause_init_modifiers (c_parser *parser, bool *target,
+				    bool *targetsync, tree *prefer_type_tree)
+{
+  *target = false;
+  *targetsync = false;
+  *prefer_type_tree = NULL_TREE;
+
+  do
+    {
+      c_token *tok = c_parser_peek_token (parser);
+      if (tok->type != CPP_NAME)
+	goto fail;
+      const char *p = IDENTIFIER_POINTER (tok->value);
+      if (strcmp ("targetsync", p) == 0)
+	{
+	  if (*targetsync)
+	    error_at (tok->location, "duplicate %<targetsync%> modifier");
+	  *targetsync = true;
+	  c_parser_consume_token (parser);
+	}
+      else if (strcmp ("target", p) == 0)
+	{
+	  if (*target)
+	    error_at (tok->location, "duplicate %<target%> modifier");
+	  *target = true;
+	  c_parser_consume_token (parser);
+	}
+      else if (strcmp ("prefer_type", p) == 0)
+	{
+	  if (*prefer_type_tree != NULL_TREE)
+	    error_at (tok->location, "duplicate %<prefer_type%> modifier");
+	  c_parser_consume_token (parser);
+	  *prefer_type_tree = c_parser_omp_modifier_prefer_type (parser);
+	  if (*prefer_type_tree == error_mark_node)
+	    return false;
+	}
+      else
+	goto fail;
+      tok = c_parser_peek_token (parser);
+      if (tok->type == CPP_COMMA)
+	{
+	  c_parser_consume_token (parser);
+	  continue;
+	}
+      /* Unknown token - either done or an error; handle it in the caller.  */
+      return true;
+    }
+  while (true);
+
+fail:
+  c_parser_error (parser, "%<init%> clause with modifier other than "
+			  "%<prefer_type%>, %<target%> or %<targetsync%>");
+  return false;
+}
+
 /* OpenMP 5.1:
    init ( [init-modifier-list : ] variable-list )
 
@@ -20647,17 +20714,17 @@ c_parser_omp_clause_init (c_parser *parser, tree list)
   if (!parens.require_open (parser))
     return list;
 
-  unsigned pos = 0, raw_pos = 1;
+  unsigned raw_pos = 1;
   while (c_parser_peek_nth_token_raw (parser, raw_pos)->type == CPP_NAME)
     {
-      pos++; raw_pos++;
+      raw_pos++;
       if (c_parser_peek_nth_token_raw (parser, raw_pos)->type == CPP_OPEN_PAREN)
 	{
 	  raw_pos++;
 	  c_parser_check_balanced_raw_token_sequence (parser, &raw_pos);
 	  if (c_parser_peek_nth_token_raw (parser, raw_pos)->type != CPP_CLOSE_PAREN)
 	    {
-	      pos = 0;
+	      raw_pos = 0;
 	      break;
 	    }
 	  raw_pos++;
@@ -20666,10 +20733,9 @@ c_parser_omp_clause_init (c_parser *parser, tree list)
 	break;
       if (c_parser_peek_nth_token_raw (parser, raw_pos)->type != CPP_COMMA)
 	{
-	  pos = 0;
+	  raw_pos = 0;
 	  break;
 	}
-      pos++;
       raw_pos++;
     }
 
@@ -20677,57 +20743,18 @@ c_parser_omp_clause_init (c_parser *parser, tree list)
   bool targetsync = false;
   tree prefer_type_tree = NULL_TREE;
 
-  for (unsigned pos2 = 0; pos2 < pos; ++pos2)
+  if (raw_pos > 1
+      && (!c_parser_omp_clause_init_modifiers (parser, &target, &targetsync,
+					       &prefer_type_tree)
+	  || !c_parser_require (parser, CPP_COLON, "expected %<:%>")))
     {
-      c_token *tok = c_parser_peek_token (parser);
-      if (tok->type == CPP_COMMA)
-	{
-	  c_parser_consume_token (parser);
-	  continue;
-	}
-
-      const char *p = IDENTIFIER_POINTER (tok->value);
-      if (strcmp ("targetsync", p) == 0)
-	{
-	  if (targetsync)
-	    error_at (tok->location, "duplicate %<targetsync%> modifier");
-	  targetsync = true;
-	  c_parser_consume_token (parser);
-	}
-      else if (strcmp ("target", p) == 0)
-	{
-	  if (target)
-	    error_at (tok->location, "duplicate %<target%> modifier");
-	  target = true;
-	  c_parser_consume_token (parser);
-	}
-      else if (strcmp ("prefer_type", p) == 0)
-	{
-	  if (prefer_type_tree != NULL_TREE)
-	    error_at (tok->location, "duplicate %<prefer_type%> modifier");
-	  c_parser_consume_token (parser);
-	  prefer_type_tree = c_parser_omp_modifier_prefer_type (parser);
-	  if (prefer_type_tree == error_mark_node)
-	    return list;
-	}
-      else
-	{
-	  c_parser_error (parser, "%<init%> clause with modifier other than "
-				  "%<prefer_type%>, %<target%> or "
-				  "%<targetsync%>");
-	  parens.skip_until_found_close (parser);
-	  return list;
-	}
-    }
-  if (pos)
-    {
-      c_token *tok = c_parser_peek_token (parser);
-      gcc_checking_assert (tok->type == CPP_COLON);
-      c_parser_consume_token (parser);
+      if (prefer_type_tree != error_mark_node)
+	parens.skip_until_found_close (parser);
+      return list;
     }
 
   tree nl = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_INIT, list,
-					 false);
+					false);
   parens.skip_until_found_close (parser);
 
   for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
@@ -20758,7 +20785,13 @@ static tree
 c_parser_omp_clause_interop (c_parser *parser, tree list)
 {
   check_no_duplicate_clause (list, OMP_CLAUSE_INTEROP, "interop");
-  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_INTEROP, list);
+  tree nl = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_INTEROP, list);
+  for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+    {
+      TREE_USED (OMP_CLAUSE_DECL (c)) = 1;
+      DECL_READ_P (OMP_CLAUSE_DECL (c)) = 1;
+    }
+  return nl;
 }
 
 /* Parse all OpenACC clauses.  The set clauses allowed by the directive
@@ -26322,7 +26355,7 @@ check_clauses:
 
    OpenMP 5.1
    # pragma omp declare variant (identifier) match(context-selector) \
-      adjust_args(adjust-op:argument-list) new-line
+      adjust_args(adjust-op:argument-list) append_args(interop-list) new-line
    */
 
 #define OMP_DECLARE_SIMD_CLAUSE_MASK				\
@@ -26786,10 +26819,15 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 
   parens.require_close (parser);
 
+  tree append_args_tree = NULL_TREE;
+  tree append_args_last;
   vec<tree> adjust_args_list = vNULL;
   bool has_match = false, has_adjust_args = false;
   location_t adjust_args_loc = UNKNOWN_LOCATION;
-  tree need_device_ptr_list = make_node (TREE_LIST);
+  location_t append_args_loc = UNKNOWN_LOCATION;
+  location_t match_loc = UNKNOWN_LOCATION;
+  tree need_device_ptr_list = NULL_TREE;
+  tree ctx = error_mark_node;
 
   do
     {
@@ -26798,26 +26836,36 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 	c_parser_consume_token (parser);
 
       const char *clause = "";
-      location_t match_loc = c_parser_peek_token (parser)->location;
+      location_t 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);
 
       enum clause
       {
 	match,
-	adjust_args
+	adjust_args,
+	append_args
       } ccode;
 
       if (strcmp (clause, "match") == 0)
-	ccode = match;
+	{
+	  ccode = match;
+	  match_loc = loc;
+	}
       else if (strcmp (clause, "adjust_args") == 0)
 	{
 	  ccode = adjust_args;
-	  adjust_args_loc = match_loc;
+	  adjust_args_loc = loc;
+	}
+      else if (strcmp (clause, "append_args") == 0)
+	{
+	  ccode = append_args;
+	  append_args_loc = loc;
 	}
       else
 	{
-	  c_parser_error (parser, "expected %<match%> clause");
+	  c_parser_error (parser, "expected %<match%>, %<adjust_args%> or "
+				  "%<append_args%> clause");
 	  goto fail;
 	}
 
@@ -26834,8 +26882,7 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 	  if (has_match)
 	    error_at (match_loc, "too many %<match%> clauses");
 	  has_match = true;
-	  tree ctx
-	    = c_parser_omp_context_selector_specification (parser, parms);
+	  ctx  = c_parser_omp_context_selector_specification (parser, parms);
 	  if (ctx == error_mark_node)
 	    goto fail;
 	  ctx = omp_check_context_selector (match_loc, ctx);
@@ -26862,40 +26909,6 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 			    variant);
 		  variant = error_mark_node;
 		}
-	      else if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
-						  OMP_TRAIT_CONSTRUCT_SIMD))
-		{
-		  if (comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
-		    {
-		      if (TYPE_ARG_TYPES (TREE_TYPE (variant)) == NULL_TREE)
-			TYPE_ARG_TYPES (TREE_TYPE (variant))
-			  = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
-		    }
-		  else
-		    {
-		      error_at (token->location,
-				"variant %qD and base %qD have "
-				"incompatible types",
-				variant, fndecl);
-		      variant = error_mark_node;
-		    }
-		}
-	      if (variant != error_mark_node)
-		{
-		  C_DECL_USED (variant) = 1;
-		  tree construct
-		    = omp_get_context_selector_list (ctx,
-						     OMP_TRAIT_SET_CONSTRUCT);
-		  omp_mark_declare_variant (match_loc, variant, construct);
-		  if (omp_context_selector_matches (ctx))
-		    {
-		      tree attr = tree_cons (get_identifier (
-					       "omp declare variant base"),
-					     build_tree_list (variant, ctx),
-					     DECL_ATTRIBUTES (fndecl));
-		      DECL_ATTRIBUTES (fndecl) = attr;
-		    }
-		}
 	    }
 	}
       else if (ccode == adjust_args)
@@ -26912,7 +26925,7 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 		  c_parser_consume_token (parser); // need_device_ptr
 		  c_parser_consume_token (parser); // :
 
-		  location_t loc = c_parser_peek_token (parser)->location;
+		  loc = c_parser_peek_token (parser)->location;
 		  tree list
 		    = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_ERROR,
 						  NULL_TREE);
@@ -26978,19 +26991,180 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 	      goto fail;
 	    }
 	}
+      else if (ccode == append_args)
+	{
+	  if (append_args_tree)
+	    error_at (append_args_loc, "too many %qs clauses", "append_args");
+	  do
+	    {
+	      location_t loc = c_parser_peek_token (parser)->location;
+	      if (!c_parser_next_token_is (parser, CPP_NAME)
+		  || strcmp ("interop",
+			     IDENTIFIER_POINTER (
+			       c_parser_peek_token (parser)->value)))
+		{
+		  error_at (loc, "expected %<interop%>");
+		  goto fail;
+		}
+	      c_parser_consume_token (parser);
+
+	      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+		goto fail;
+	      bool target = false;
+	      bool targetsync = false;
+	      tree prefer_type_tree = NULL_TREE;
+	      if (!c_parser_omp_clause_init_modifiers (parser, &target,
+						       &targetsync,
+						       &prefer_type_tree)
+		  || !c_parser_require (parser, CPP_CLOSE_PAREN,
+					"expected %<)%> or %<,%>"))
+		goto fail;
+	      tree t = build_omp_clause (loc, OMP_CLAUSE_INIT);
+	      if (append_args_tree)
+		OMP_CLAUSE_CHAIN (append_args_last) = t;
+	      else
+		append_args_tree = append_args_last = t;
+	      if (target)
+		OMP_CLAUSE_INIT_TARGET (t) = 1;
+	      if (targetsync)
+		OMP_CLAUSE_INIT_TARGETSYNC (t) = 1;
+	      if (prefer_type_tree)
+		OMP_CLAUSE_INIT_PREFER_TYPE (t) = prefer_type_tree;
+	      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+		break;
+	      if (!c_parser_require (parser, CPP_COMMA, "expected %<)%> or %<,%>"))
+		goto fail;
+	    }
+	  while (true);
+	}
 
       parens.require_close (parser);
   } while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL));
   c_parser_skip_to_pragma_eol (parser);
 
-  if (has_adjust_args)
+  if ((ctx != error_mark_node && variant != error_mark_node)
+      && !omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
+				    OMP_TRAIT_CONSTRUCT_SIMD))
+    {
+      bool fail = false;
+      if (append_args_tree
+	  && TYPE_ARG_TYPES (TREE_TYPE (fndecl)) != NULL_TREE
+	  && TYPE_ARG_TYPES (TREE_TYPE (variant)) != NULL_TREE)
+	{
+	  int nappend_args = 0;
+	  int nbase_args = 0;
+	  for (tree t = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+	       t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t))
+	    nbase_args++;
+	  for (tree t = append_args_tree; t; t = TREE_CHAIN (t))
+	    nappend_args++;
+
+	  tree args, arg;
+	  args = arg = TYPE_ARG_TYPES (TREE_TYPE (variant));
+	  for (int j = 0; j < nbase_args && arg; j++, arg = TREE_CHAIN (arg))
+	    args = arg;
+	  for (int i = 0; i < nappend_args && arg; i++)
+	    arg = TREE_CHAIN (arg);
+	  tree saved_args;
+	  if (nbase_args)
+	    {
+	      saved_args = TREE_CHAIN (args);
+	      TREE_CHAIN (args) = arg;
+	    }
+	  else
+	    {
+	      saved_args = args;
+	      TYPE_ARG_TYPES (TREE_TYPE (variant)) = arg;
+	    }
+	  if (!comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
+	    fail = true;
+	  if (nbase_args)
+	    TREE_CHAIN (args) = saved_args;
+	  else
+	    TYPE_ARG_TYPES (TREE_TYPE (variant)) = saved_args;
+	  arg = saved_args;
+	  if (!fail)
+	    for (int i = 0; i < nappend_args; i++, arg = TREE_CHAIN (arg))
+	      if (!arg || !c_omp_interop_t_p (TREE_VALUE (arg)))
+		{
+		  error_at (DECL_SOURCE_LOCATION (variant),
+			    "argument %d of %qD must be of %<omp_interop_t%>",
+			    nbase_args + i + 1, variant);
+		  inform (append_args_loc, "%<append_args%> specified here");
+		  break;
+		}
+	}
+      else
+	{
+	  if (comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant)))
+	    {
+	      if (TYPE_ARG_TYPES (TREE_TYPE (variant)) == NULL_TREE
+		  && TYPE_ARG_TYPES (TREE_TYPE (fndecl)) != NULL_TREE)
+		{
+		  if (!append_args_tree)
+		    TYPE_ARG_TYPES (TREE_TYPE (variant))
+		      = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+		  else
+		    {
+		      tree new_args = NULL_TREE;
+		      tree arg, last_arg = NULL_TREE;
+		      for (arg = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+			   arg && arg != void_type_node; arg = TREE_CHAIN (arg))
+			{
+			  if (new_args == NULL_TREE)
+			    new_args = last_arg = copy_node (arg);
+			  else
+			    {
+			      TREE_CHAIN (last_arg) = copy_node (arg);
+			      last_arg = TREE_CHAIN (last_arg);
+			    }
+			}
+		      for (tree t3 = append_args_tree; t3; t3 = TREE_CHAIN (t3))
+			{
+			  tree type = lookup_name (get_identifier ("omp_interop_t"));
+			  type = type ? TREE_TYPE (type) : ptr_type_node;
+			  last_arg = tree_cons (NULL_TREE, type, last_arg);
+			}
+		      TREE_CHAIN (last_arg) = arg;
+		      TYPE_ARG_TYPES (TREE_TYPE (variant)) = new_args;
+		    }
+		}
+	    }
+	  else
+	    fail = true;
+	}
+      if (fail)
+	{
+	  error_at (token->location,
+		    "variant %qD and base %qD have incompatible types",
+		    variant, fndecl);
+	  variant = error_mark_node;
+	}
+    }
+  if (ctx != error_mark_node && variant != error_mark_node)
+    {
+      C_DECL_USED (variant) = 1;
+      tree construct = omp_get_context_selector_list (ctx,
+						      OMP_TRAIT_SET_CONSTRUCT);
+      omp_mark_declare_variant (match_loc, variant, construct);
+      if (omp_context_selector_matches (ctx))
+	{
+	  tree attr = tree_cons (get_identifier ("omp declare variant base"),
+				 build_tree_list (variant, ctx),
+				 DECL_ATTRIBUTES (fndecl));
+	  DECL_ATTRIBUTES (fndecl) = attr;
+	}
+    }
+
+  if (has_adjust_args || append_args_tree)
     {
       if (!has_match)
 	{
-	  error_at (adjust_args_loc,
-		    "an %<adjust_args%> clause requires a %<match%> clause");
+	  error_at (has_adjust_args ? adjust_args_loc : append_args_loc,
+		    "an %qs clause requires a %<match%> clause",
+		    has_adjust_args ? "adjust_args" : "append_args");
 	}
-      else
+      else if (ctx != error_mark_node && variant != error_mark_node)
 	{
 	  tree attr = lookup_attribute ("omp declare variant base",
 					DECL_ATTRIBUTES (fndecl));
@@ -26999,23 +27173,24 @@ c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
 	      tree ctx = TREE_VALUE (TREE_VALUE (attr));
 	      if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
 					     OMP_TRAIT_CONSTRUCT_DISPATCH))
-		error_at (
-		  adjust_args_loc,
-		  "an %<adjust_args%> clause can only be specified if the "
-		  "%<dispatch%> selector of the %<construct%> selector set "
-		  "appears in the %<match%> clause");
+		error_at (has_adjust_args ? adjust_args_loc : append_args_loc,
+			  "an %qs clause can only be specified if the "
+			  "%<dispatch%> selector of the %<construct%> selector "
+			  "set appears in the %<match%> clause",
+			  has_adjust_args ? "adjust_args" : "append_args");
 	    }
 	}
     }
 
-  if (TREE_CHAIN (need_device_ptr_list) != NULL_TREE
-      && variant != error_mark_node)
+  if ((ctx != error_mark_node && variant != error_mark_node)
+      && (need_device_ptr_list || append_args_tree))
     {
       tree variant_decl = tree_strip_nop_conversions (variant);
+      tree t = build_tree_list (need_device_ptr_list,
+				NULL_TREE /* need_device_addr */);
+      TREE_CHAIN (t) = append_args_tree;
       DECL_ATTRIBUTES (variant_decl)
-	= tree_cons (get_identifier ("omp declare variant variant adjust_args"),
-		     build_tree_list (need_device_ptr_list,
-				      NULL_TREE /*need_device_addr */),
+	= tree_cons (get_identifier ("omp declare variant variant args"), t,
 		     DECL_ATTRIBUTES (variant_decl));
     }
 }
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a1b9957a9be..e8d92147aa1 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8461,6 +8461,39 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
     else
       vec_safe_push (args, build_zero_cst (TREE_TYPE (parm)));
 
+  unsigned nappend_args = 0;
+  tree append_args_list = TREE_CHAIN (TREE_CHAIN (chain));
+  if (append_args_list)
+    {
+      append_args_list = TREE_VALUE (append_args_list);
+      if (append_args_list)
+	append_args_list = TREE_CHAIN (append_args_list);
+      for (tree t = append_args_list; t; t = TREE_CHAIN (t))
+	nappend_args++;
+      if (nappend_args)
+	{
+	  tree type;
+	  if ((type = lookup_qualified_name (current_scope (),
+					     "omp_interop_t",
+					     LOOK_want::NORMAL,
+					     /*complain*/false)) == NULL_TREE
+	      || !c_omp_interop_t_p (TREE_TYPE (type)))
+	    {
+	      variant = tree_strip_any_location_wrapper (variant);
+	      if (TREE_CODE (variant) == OVERLOAD && OVL_SINGLE_P (variant))
+		variant = OVL_FIRST (variant);
+	      error_at (EXPR_LOC_OR_LOC (variant, DECL_SOURCE_LOCATION (variant)),
+			"argument %d of %qE must be of %<omp_interop_t%>",
+			args->length () + 1, variant);
+	      inform (OMP_CLAUSE_LOCATION (append_args_list),
+			"%<append_args%> specified here");
+	      return true;
+	    }
+	  for (unsigned i = 0; i < nappend_args; i++)
+	    vec_safe_push (args, build_zero_cst (TREE_TYPE (type)));
+	}
+    }
+
   bool koenig_p = false;
   if (idk == CP_ID_KIND_UNQUALIFIED || idk == CP_ID_KIND_TEMPLATE_ID)
     {
@@ -8510,8 +8543,57 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
 
   if (variant)
     {
+      bool fail;
       const char *varname = IDENTIFIER_POINTER (DECL_NAME (variant));
-      if (!comptypes (TREE_TYPE (decl), TREE_TYPE (variant), 0))
+      if (!nappend_args)
+	fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant),
+			   COMPARE_STRICT);
+      else
+	{
+	  unsigned nbase_args = 0;
+	  for (tree t = TYPE_ARG_TYPES (TREE_TYPE (decl));
+	       t && TREE_VALUE (t) != void_type_node; t = TREE_CHAIN (t))
+	    nbase_args++;
+	  tree vargs, varg;
+	  vargs = varg = TYPE_ARG_TYPES (TREE_TYPE (variant));
+	  for (unsigned i = 0; i < nbase_args && varg;
+	       i++, varg = TREE_CHAIN (varg))
+	    vargs = varg;
+	  for (unsigned i = 0; i < nappend_args && varg; i++)
+	    varg = TREE_CHAIN (varg);
+	  tree saved_vargs;
+	  if (nbase_args)
+	    {
+	      saved_vargs = TREE_CHAIN (vargs);
+	      TREE_CHAIN (vargs) = varg;
+	    }
+	  else
+	    {
+	      saved_vargs = vargs;
+	      TYPE_ARG_TYPES (TREE_TYPE (variant)) = varg;
+	    }
+	  /* Skip assert check that TYPE_CANONICAL is the same.  */
+	  fail = !comptypes (TREE_TYPE (decl), TREE_TYPE (variant),
+			     COMPARE_STRUCTURAL);
+	  if (nbase_args)
+	    TREE_CHAIN (vargs) = saved_vargs;
+	  else
+	    TYPE_ARG_TYPES (TREE_TYPE (variant)) = saved_vargs;
+	  varg = saved_vargs;
+	  if (!fail && !processing_template_decl)
+	    for (unsigned i = 0; i < nappend_args;
+		 i++, varg = TREE_CHAIN (varg))
+	      if (!varg || !c_omp_interop_t_p (TREE_VALUE (varg)))
+		{
+		  error_at (DECL_SOURCE_LOCATION (variant),
+			    "argument %d of %qD must be of %<omp_interop_t%>",
+			    nbase_args + i + 1, variant);
+		  inform (OMP_CLAUSE_LOCATION (append_args_list),
+			  "%<append_args%> specified here");
+		  break;
+		}
+	}
+      if (fail)
 	{
 	  error_at (varid_loc, "variant %qD and base %qD have incompatible "
 			       "types", variant, decl);
@@ -8538,7 +8620,7 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
 	  tree adjust_args_list = TREE_CHAIN (TREE_CHAIN (chain));
 	  if (adjust_args_list != NULL_TREE)
 	    DECL_ATTRIBUTES (variant) = tree_cons (
-	      get_identifier ("omp declare variant variant adjust_args"),
+	      get_identifier ("omp declare variant variant args"),
 	      TREE_VALUE (adjust_args_list), DECL_ATTRIBUTES (variant));
 	}
     }
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 88641c373e2..cb160dacf98 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -42407,6 +42407,23 @@ cp_parser_omp_clause_proc_bind (cp_parser *parser, tree list,
   return list;
 }
 
+/* OpenMP 5.1:
+   interop (var-list)  */
+
+static tree
+cp_parser_omp_clause_interop (cp_parser *parser, tree list,
+			      location_t location)
+{
+  check_no_duplicate_clause (list, OMP_CLAUSE_INTEROP, "interop", location);
+  tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_INTEROP, list);
+  for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+    {
+      TREE_USED (OMP_CLAUSE_DECL (c)) = 1;
+      DECL_READ_P (OMP_CLAUSE_DECL (c)) = 1;
+    }
+  return nl;
+}
+
 /* OpenMP 5.0:
    device_type ( host | nohost | any )  */
 
@@ -42761,6 +42778,73 @@ cp_parser_omp_modifier_prefer_type (cp_parser *parser)
   return res;
 }
 
+/* OpenMP 5.1
+     modifiers of the 'init' clause, used by the 'init' and the
+     'append_args' clauses.
+
+   Modifiers:
+     target
+     targetsync
+     prefer_type (preference-specification)
+
+  Returns 'false' if an error has been issued.  */
+
+static bool
+cp_parser_omp_clause_init_modifiers (cp_parser *parser, bool *target,
+				     bool *targetsync, tree *prefer_type_tree)
+{
+  *target = false;
+  *targetsync = false;
+  *prefer_type_tree = NULL_TREE;
+
+  do
+    {
+      cp_token *tok = cp_lexer_peek_token (parser->lexer);
+      if (tok->type != CPP_NAME)
+	goto fail;
+      const char *p = IDENTIFIER_POINTER (tok->u.value);
+      if (strcmp ("targetsync", p) == 0)
+	{
+	  if (*targetsync)
+	    error_at (tok->location, "duplicate %<targetsync%> modifier");
+	  *targetsync = true;
+	  cp_lexer_consume_token (parser->lexer);
+	}
+      else if (strcmp ("target", p) == 0)
+	{
+	  if (*target)
+	    error_at (tok->location, "duplicate %<target%> modifier");
+	  *target = true;
+	  cp_lexer_consume_token (parser->lexer);
+	}
+      else if (strcmp ("prefer_type", p) == 0)
+	{
+	  if (*prefer_type_tree != NULL_TREE)
+	    error_at (tok->location, "duplicate %<prefer_type%> modifier");
+	  cp_lexer_consume_token (parser->lexer);
+	  *prefer_type_tree = cp_parser_omp_modifier_prefer_type (parser);
+	  if (*prefer_type_tree == error_mark_node)
+	    return false;
+	}
+      else
+	goto fail;
+      tok = cp_lexer_peek_token (parser->lexer);
+      if (tok->type == CPP_COMMA)
+	{
+	  cp_lexer_consume_token (parser->lexer);
+	  continue;
+	}
+      /* Unknown token - either done or an error; handle it in the caller.  */
+      return true;
+    }
+  while (true);
+
+fail:
+  cp_parser_error (parser, "%<init%> clause with modifier other than "
+			   "%<prefer_type%>, %<target%> or %<targetsync%>");
+  return false;
+}
+
 /* OpenMP 5.1:
    init ( [init-modifier-list : ] variable-list )
 
@@ -42775,17 +42859,17 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
   if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
     return list;
 
-  unsigned pos = 0, raw_pos = 1;
+  unsigned raw_pos = 1;
   while (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type == CPP_NAME)
     {
-      pos++; raw_pos++;
+      raw_pos++;
       if (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type
 	  == CPP_OPEN_PAREN)
 	{
 	  unsigned n = cp_parser_skip_balanced_tokens (parser, raw_pos);
 	  if (n == raw_pos)
 	    {
-	      pos = 0;
+	      raw_pos = 0;
 	      break;
 	    }
 	  raw_pos = n;
@@ -42794,10 +42878,9 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
 	break;
       if (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type != CPP_COMMA)
 	{
-	  pos = 0;
+	  raw_pos = 0;
 	  break;
 	}
-      pos++;
       raw_pos++;
     }
 
@@ -42805,54 +42888,17 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
   bool targetsync = false;
   tree prefer_type_tree = NULL_TREE;
 
-  for (unsigned pos2 = 0; pos2 < pos; ++pos2)
+  if (raw_pos > 1
+      && (!cp_parser_omp_clause_init_modifiers (parser, &target, &targetsync,
+					       &prefer_type_tree)
+	  || !cp_parser_require (parser, CPP_COLON, RT_COLON)))
     {
-      cp_token *tok = cp_lexer_peek_token (parser->lexer);
-      if (tok->type == CPP_COMMA)
-	{
-	  cp_lexer_consume_token (parser->lexer);
-	  continue;
-	}
-      const char *p = IDENTIFIER_POINTER (tok->u.value);
-      if (strcmp ("targetsync", p) == 0)
-	{
-	  if (targetsync)
-	    error_at (tok->location, "duplicate %<targetsync%> modifier");
-	  targetsync = true;
-	  cp_lexer_consume_token (parser->lexer);
-	}
-      else if (strcmp ("target", p) == 0)
-	{
-	  if (target)
-	    error_at (tok->location, "duplicate %<target%> modifier");
-	  target = true;
-	  cp_lexer_consume_token (parser->lexer);
-	}
-      else if (strcmp ("prefer_type", p) == 0)
-	{
-	  if (prefer_type_tree != NULL_TREE)
-	    error_at (tok->location, "duplicate %<prefer_type%> modifier");
-	  cp_lexer_consume_token (parser->lexer);
-	  prefer_type_tree = cp_parser_omp_modifier_prefer_type (parser);
-	  if (prefer_type_tree == error_mark_node)
-	    return error_mark_node;
-	}
-      else
-	{
-	  cp_parser_error (parser, "%<init%> clause with modifier other than "
-				   "%<prefer_type%>, %<target%> or "
-				   "%<targetsync%>");
-	  cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
-						 /*or_comma=*/false,
-						 /*consume_paren=*/true);
-	  return list;
-	}
-    }
-  if (pos)
-    {
-      gcc_checking_assert (cp_lexer_peek_token (parser->lexer)->type
-			   == CPP_COLON);
-      cp_lexer_consume_token (parser->lexer);
+      if (prefer_type_tree == error_mark_node)
+	return error_mark_node;
+      cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+					     /*or_comma=*/false,
+					     /*consume_paren=*/true);
+      return list;
     }
 
   tree nl = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_INIT, list,
@@ -42860,11 +42906,11 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
   for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
     {
       if (target)
-       OMP_CLAUSE_INIT_TARGET (c) = 1;
+	OMP_CLAUSE_INIT_TARGET (c) = 1;
       if (targetsync)
-       OMP_CLAUSE_INIT_TARGETSYNC (c) = 1;
+	OMP_CLAUSE_INIT_TARGETSYNC (c) = 1;
       if (prefer_type_tree)
-       OMP_CLAUSE_INIT_PREFER_TYPE (c) = prefer_type_tree;
+	OMP_CLAUSE_INIT_PREFER_TYPE (c) = prefer_type_tree;
     }
   return nl;
 }
@@ -43516,10 +43562,8 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 	  c_name = "use";
 	  break;
 	case PRAGMA_OMP_CLAUSE_INTEROP:
-	  check_no_duplicate_clause (clauses, OMP_CLAUSE_INTEROP, "interop",
-				     token->location);
-	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_INTEROP,
-					    clauses);
+	  clauses = cp_parser_omp_clause_interop (parser, clauses,
+						  token->location);
 	  c_name = "interop";
 	  break;
 	case PRAGMA_OMP_CLAUSE_DETACH:
@@ -50066,10 +50110,14 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
   location_t finish_loc = get_finish (varid.get_location ());
   location_t varid_loc = make_location (caret_loc, start_loc, finish_loc);
 
+  tree append_args_tree = NULL_TREE;
+  tree append_args_last;
   vec<tree> adjust_args_list = vNULL;
   bool has_match = false, has_adjust_args = false;
   location_t adjust_args_loc = UNKNOWN_LOCATION;
-  tree need_device_ptr_list = make_node (TREE_LIST);
+  location_t append_args_loc = UNKNOWN_LOCATION;
+  tree need_device_ptr_list = NULL_TREE;
+  tree ctx = NULL_TREE;
 
   do
     {
@@ -50086,7 +50134,8 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
       enum clause
       {
 	match,
-	adjust_args
+	adjust_args,
+	append_args
       } ccode;
 
       if (strcmp (clause, "match") == 0)
@@ -50096,9 +50145,15 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
 	  ccode = adjust_args;
 	  adjust_args_loc = match_loc;
 	}
+      else if (strcmp (clause, "append_args") == 0)
+	{
+	  ccode = append_args;
+	  append_args_loc = match_loc;
+	}
       else
 	{
-	  cp_parser_error (parser, "expected %<match%> clause");
+	  cp_parser_error (parser, "expected %<match%>, %<adjust_args%> or "
+				   "%<append_args%> clause");
 	  goto fail;
 	}
 
@@ -50112,8 +50167,7 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
 	  if (has_match)
 	    error_at (match_loc, "too many %<match%> clauses");
 	  has_match = true;
-	  tree ctx
-	    = cp_parser_omp_context_selector_specification (parser, true);
+	  ctx = cp_parser_omp_context_selector_specification (parser, true);
 	  if (ctx == error_mark_node)
 	    goto fail;
 	  ctx = omp_check_context_selector (match_loc, ctx);
@@ -50219,40 +50273,95 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
 	      goto fail;
 	    }
 	}
+      else if (ccode == append_args)
+	{
+	  if (append_args_tree)
+	     error_at (append_args_loc, "too many %qs clauses", "append_args");
+	  do
+	    {
+	      location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+	      if (!cp_lexer_next_token_is (parser->lexer, CPP_NAME)
+		  || strcmp ("interop",
+			     IDENTIFIER_POINTER (
+			       cp_lexer_peek_token (parser->lexer)->u.value)))
+		{
+		  error_at (loc, "expected %<interop%>");
+		  goto fail;
+		}
+	      cp_lexer_consume_token (parser->lexer); // :
+
+	      if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+		goto fail;
+	      bool target = false;
+	      bool targetsync = false;
+	      tree prefer_type_tree = NULL_TREE;
+	      if (!cp_parser_omp_clause_init_modifiers (parser, &target,
+							&targetsync,
+							&prefer_type_tree))
+		goto fail;
+	      tree t = build_omp_clause (loc, OMP_CLAUSE_INIT);
+	      if (append_args_tree)
+		OMP_CLAUSE_CHAIN (append_args_last) = t;
+	      else
+		append_args_tree = append_args_last = t;
+	      if (target)
+		OMP_CLAUSE_INIT_TARGET (t) = 1;
+	      if (targetsync)
+		OMP_CLAUSE_INIT_TARGETSYNC (t) = 1;
+	      if (prefer_type_tree)
+		OMP_CLAUSE_INIT_PREFER_TYPE (t) = prefer_type_tree;
+	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN))
+		{
+		  cp_parser_error (parser, "expected %<)%> or %<,%>");
+		  goto fail;
+		}
+	      cp_lexer_consume_token (parser->lexer); // ')'
+	      if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+		{
+		  cp_lexer_consume_token (parser->lexer); // ','
+		  break;
+		}
+	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+		{
+		  cp_parser_error (parser, "expected %<)%> or %<,%>");
+		  goto fail;
+		}
+	      cp_lexer_consume_token (parser->lexer); // ','
+	    }
+	  while (true);
+	}
   } while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL));
 
-  if (has_adjust_args)
+  if ((ctx != error_mark_node && variant != error_mark_node)
+      && (has_adjust_args || append_args_tree))
     {
       if (!has_match)
 	{
-	  error_at (adjust_args_loc,
-		    "an %<adjust_args%> clause requires a %<match%> clause");
+	  error_at (has_adjust_args ? adjust_args_loc : append_args_loc,
+		    "an %qs clause requires a %<match%> clause",
+		    has_adjust_args ? "adjust_args" : "append_args");
 	}
       else
 	{
-	  tree ctx = TREE_VALUE (TREE_VALUE (attrs));
+	  gcc_assert (TREE_PURPOSE (attrs)
+		      == get_identifier ("omp declare variant base"));
+	  gcc_assert (TREE_PURPOSE (TREE_VALUE (attrs)) == variant);
+	  ctx = TREE_VALUE (TREE_VALUE (attrs));
 	  if (!omp_get_context_selector (ctx, OMP_TRAIT_SET_CONSTRUCT,
 					 OMP_TRAIT_CONSTRUCT_DISPATCH))
-	    error_at (
-	      adjust_args_loc,
-	      "an %<adjust_args%> clause can only be specified if the "
-	      "%<dispatch%> selector of the construct selector set appears "
-	      "in the %<match%> clause");
-	  else if (TREE_CHAIN (need_device_ptr_list) != NULL_TREE)
-	    {
-	      // We might not have a DECL for the variant yet. So we store the
-	      // need_device_ptr list in the base function attribute, after loc
-	      // nodes.
-	      gcc_assert (TREE_PURPOSE (attrs)
-			  == get_identifier ("omp declare variant base"));
-	      gcc_assert (TREE_PURPOSE (TREE_VALUE (attrs)) == variant);
-	      TREE_VALUE (attrs) = chainon (
-		TREE_VALUE (attrs),
-		build_tree_list (
-		  NULL_TREE,
-		  build_tree_list (need_device_ptr_list,
-				   NULL_TREE /*need_device_addr */)));
-	    }
+	    error_at (has_adjust_args ? adjust_args_loc : append_args_loc,
+		      "an %qs clause can only be specified if the %<dispatch%> "
+		      "selector of the construct selector set appears "
+		      "in the %<match%> clause",
+		      has_adjust_args ? "adjust_args" : "append_args");
+	  // We might not have a DECL for the variant yet. So we store the
+	  // need_device_ptr list in the base function attribute, after loc
+	  // nodes.
+	  tree t = build_tree_list (need_device_ptr_list,
+				    NULL_TREE /* need_device_addr */);
+	  TREE_CHAIN (t) = append_args_tree;
+	  TREE_VALUE (attrs) = chainon (TREE_VALUE (attrs),
+					build_tree_list ( NULL_TREE, t));
 	}
     }
 
@@ -51299,7 +51408,7 @@ cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok,
 
    OpenMP 5.1
    #pragma omp declare variant (identifier) match (context-selector) \
-      adjust_args (adjust-op:argument-list)  */
+      adjust_args (adjust-op:argument-list) append_args(interop-list)  */
 
 static bool
 cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 8a91c9ce9f6..648eceac2f5 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -12094,6 +12094,15 @@ tsubst_attribute (tree t, tree *decl_p, tree args,
       tree chain = TREE_CHAIN (val);
       location_t match_loc = cp_expr_loc_or_input_loc (TREE_PURPOSE (chain));
       tree ctx = copy_list (TREE_VALUE (val));
+      tree append_args_list = TREE_CHAIN (TREE_CHAIN (chain));
+      if (append_args_list)
+	{
+	  append_args_list = TREE_VALUE (append_args_list);
+	  if (append_args_list)
+	    TREE_CHAIN (append_args_list)
+	      = tsubst_omp_clauses (TREE_CHAIN (append_args_list),
+				    C_ORT_OMP_DECLARE_SIMD, args, complain, in_decl);
+	}
       for (tree tss = ctx; tss; tss = TREE_CHAIN (tss))
 	{
 	  enum omp_tss_code set = OMP_TSS_CODE (tss);
@@ -17916,7 +17925,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 			     complain, in_decl);
 	  break;
 	case OMP_CLAUSE_INIT:
-	  if (ort == C_ORT_OMP_INTEROP
+	  if ((ort == C_ORT_OMP_INTEROP  || ort == C_ORT_OMP_DECLARE_SIMD)
 	      && OMP_CLAUSE_INIT_PREFER_TYPE (nc)
 	      && TREE_CODE (OMP_CLAUSE_INIT_PREFER_TYPE (nc)) == TREE_LIST
 	      && (OMP_CLAUSE_CHAIN (nc)  == NULL_TREE
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index bf1c590d8e6..b1ef23be43b 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -4096,24 +4096,126 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
      vars there.  */
   bool returns_twice = call_expr_flags (*expr_p) & ECF_RETURNS_TWICE;
 
+  tree dispatch_device_num = NULL_TREE;
   tree dispatch_interop = NULL_TREE;
+  tree dispatch_append_args = NULL_TREE;
+  tree dispatch_adjust_args_list = NULL_TREE;
   if (flag_openmp
       && gimplify_omp_ctxp != NULL
       && gimplify_omp_ctxp->code == OMP_DISPATCH
-      && gimplify_omp_ctxp->clauses
-      && (dispatch_interop = omp_find_clause (gimplify_omp_ctxp->clauses,
-					      OMP_CLAUSE_INTEROP)) != NULL_TREE)
-    /* FIXME: When implementing 'append_args, use the 'device_num' of
-       the argument.  */
-    error_at (OMP_CLAUSE_LOCATION (dispatch_interop),
-	      "number of list items in %<interop%> clause exceeds number of "
-	      "%<append_args%> items for %<declare variant%> candidate %qD",
-	      fndecl);
+      && !gimplify_omp_ctxp->in_call_args
+      && EXPR_P (CALL_EXPR_FN (*expr_p))
+      && DECL_P (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0)))
+    {
+      tree fndecl = TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0);
+      dispatch_adjust_args_list
+	= lookup_attribute ("omp declare variant variant args",
+	    DECL_ATTRIBUTES (fndecl));
+      if (dispatch_adjust_args_list)
+	{
+	  dispatch_adjust_args_list = TREE_VALUE (dispatch_adjust_args_list);
+	  dispatch_append_args = TREE_CHAIN (dispatch_adjust_args_list);
+	  if (TREE_PURPOSE (dispatch_adjust_args_list) == NULL_TREE
+	      && TREE_VALUE (dispatch_adjust_args_list) == NULL_TREE)
+	    dispatch_adjust_args_list = NULL_TREE;
+	}
+      dispatch_device_num = omp_find_clause (gimplify_omp_ctxp->clauses,
+					     OMP_CLAUSE_DEVICE);
+      if (dispatch_device_num)
+	dispatch_device_num = OMP_CLAUSE_DEVICE_ID (dispatch_device_num);
+      if (gimplify_omp_ctxp->clauses)
+	dispatch_interop = omp_find_clause (gimplify_omp_ctxp->clauses,
+					    OMP_CLAUSE_INTEROP);
+      /* Already processed?  */
+      if (dispatch_interop
+	  && OMP_CLAUSE_DECL (dispatch_interop) == NULL_TREE)
+	dispatch_interop = dispatch_append_args = NULL_TREE;
+
+      int nappend = 0, ninterop = 0;
+      for (tree t = dispatch_append_args; t; t = TREE_CHAIN (t))
+	nappend++;
+      if (dispatch_interop)
+	{
+	  for (tree t = dispatch_interop; t; t = TREE_CHAIN (t))
+	    if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_INTEROP)
+	      ninterop++;
+	  if (nappend < ninterop)
+	    {
+	      error_at (OMP_CLAUSE_LOCATION (dispatch_interop),
+			"number of list items in %<interop%> clause (%d) "
+			"exceeds the number of %<append_args%> items (%d) for "
+			"%<declare variant%> candidate %qD",
+			ninterop, nappend, fndecl);
+	      inform (dispatch_append_args
+		      ? OMP_CLAUSE_LOCATION (dispatch_append_args)
+		      : DECL_SOURCE_LOCATION (fndecl),
+		      "%<declare variant%> candidate %qD declared here",
+		      fndecl);
+	    }
+	  /* FIXME: obtain the device_number from 1st 'interop' clause item.  */
+	}
+      if (dispatch_append_args && (nappend != ninterop || !dispatch_device_num))
+	{
+	  sorry_at (OMP_CLAUSE_LOCATION (dispatch_append_args),
+		    "%<append_args%> clause not yet supported for %qD", fndecl);
+	  inform (gimplify_omp_ctxp->location,
+		    "required by %<dispatch%> construct");
+	}
+      else if (dispatch_append_args)
+	{
+	  // Append interop objects
+	  int last_arg = 0;
+	  for (tree t = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+	       t && TREE_VALUE(t) != void_type_node; t = TREE_CHAIN (t))
+	    last_arg++;
+	  last_arg = last_arg - nappend;
+
+	  int nvariadic = nargs - last_arg;
+	  nargs = last_arg + nappend + nvariadic;
+	  tree *buffer = XALLOCAVEC (tree, nargs);
+	  int i;
+	  for (i = 0; i < last_arg; i++)
+	    buffer[i] = CALL_EXPR_ARG (*expr_p, i);
+	  int j = nappend;
+	  for (tree t = dispatch_interop;
+	       t; t = TREE_CHAIN (t))
+	    if (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_INTEROP)
+	      buffer[i + --j] = OMP_CLAUSE_DECL (t);
+	  i += nappend;
+	  for (j = last_arg; j < last_arg + nvariadic; j++)
+	    buffer[i++] = CALL_EXPR_ARG (*expr_p, j);
+	  tree call = *expr_p;
+	  *expr_p = build_call_array_loc (loc, TREE_TYPE (call),
+					  CALL_EXPR_FN (call),
+					  nargs, buffer);
+
+	  /* Copy all CALL_EXPR flags.  */
+	  CALL_EXPR_STATIC_CHAIN (*expr_p) = CALL_EXPR_STATIC_CHAIN (call);
+	  CALL_EXPR_TAILCALL (*expr_p) = CALL_EXPR_TAILCALL (call);
+	  CALL_EXPR_RETURN_SLOT_OPT (*expr_p)
+	    = CALL_EXPR_RETURN_SLOT_OPT (call);
+	  CALL_FROM_THUNK_P (*expr_p) = CALL_FROM_THUNK_P (call);
+	  SET_EXPR_LOCATION (*expr_p, EXPR_LOCATION (call));
+	  CALL_EXPR_VA_ARG_PACK (*expr_p) = CALL_EXPR_VA_ARG_PACK (call);
+
+	  /* Mark as already processed.  */
+	  if (dispatch_interop)
+	    OMP_CLAUSE_DECL (dispatch_interop) = NULL_TREE;
+	  else
+	    {
+	      tree t = build_omp_clause (loc, OMP_CLAUSE_INTEROP);
+	      TREE_CHAIN (t) = gimplify_omp_ctxp->clauses;
+	      gimplify_omp_ctxp->clauses = t;
+	    }
+
+	  /* Re-run.  */
+	  return GS_OK;
+	}
+    }
 
   /* Gimplify the function arguments.  */
   if (nargs > 0)
     {
-      tree device_num = NULL_TREE;
       for (i = (PUSH_ARGS_REVERSED ? nargs - 1 : 0);
 	   PUSH_ARGS_REVERSED ? i >= 0 : i < nargs;
 	   PUSH_ARGS_REVERSED ? i-- : i++)
@@ -4125,18 +4227,7 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 	  if ((i != 1) || !builtin_va_start_p)
 	    {
 	      tree *arg_p = &CALL_EXPR_ARG (*expr_p, i);
-	      tree adjust_args_list;
-	      if (flag_openmp && gimplify_omp_ctxp != NULL
-		  && gimplify_omp_ctxp->code == OMP_DISPATCH
-		  && !gimplify_omp_ctxp->in_call_args
-		  && !integer_zerop (*arg_p)
-		  && EXPR_P (CALL_EXPR_FN (*expr_p))
-		  && DECL_P (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0))
-		  && (adjust_args_list = lookup_attribute (
-			"omp declare variant variant adjust_args",
-			DECL_ATTRIBUTES (
-			  TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0))))
-		      != NULL_TREE)
+	      if (dispatch_adjust_args_list && !integer_zerop (*arg_p))
 		{
 		  tree arg_types = TYPE_ARG_TYPES (
 		    TREE_TYPE (TREE_OPERAND (CALL_EXPR_FN (*expr_p), 0)));
@@ -4150,10 +4241,10 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 		      bool need_device_addr = false;
 		      for (int need_addr = 0; need_addr <= 1; need_addr++)
 			for (tree arg = need_addr
-					? TREE_VALUE (TREE_VALUE (
-					    adjust_args_list))
-					: TREE_PURPOSE (TREE_VALUE (
-					    adjust_args_list));
+					? TREE_VALUE (
+					    dispatch_adjust_args_list)
+					: TREE_PURPOSE (
+					    dispatch_adjust_args_list);
 			     arg != NULL; arg = TREE_CHAIN (arg))
 			  {
 			    if (TREE_VALUE (arg)
@@ -4204,6 +4295,7 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 					      "%<need_device_addr%>",
 					       OMP_CLAUSE_DECL (c));
 					  is_device_ptr = true;
+					  break;
 					}
 				      else if (decl1 == decl2)
 					{
@@ -4217,36 +4309,36 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 					      "%<need_device_ptr%>",
 					      OMP_CLAUSE_DECL (c));
 					  has_device_addr = true;
+					  break;
 					}
 				    }
 				}
-			      else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE)
-				device_num = OMP_CLAUSE_OPERAND (c, 0);
 			    }
 
 			  if ((need_device_ptr && !is_device_ptr)
 			      || (need_device_addr && !has_device_addr))
 			    {
-			      if (device_num == NULL_TREE)
+			      if (dispatch_device_num == NULL_TREE)
 				{
 				  // device_num = omp_get_default_device ()
 				  tree fn = builtin_decl_explicit (
 				    BUILT_IN_OMP_GET_DEFAULT_DEVICE);
 				  gcall *call = gimple_build_call (fn, 0);
-				  device_num = create_tmp_var (
+				  dispatch_device_num = create_tmp_var (
 				    gimple_call_return_type (call));
-				  gimple_call_set_lhs (call, device_num);
+				  gimple_call_set_lhs (call,
+						       dispatch_device_num);
 				  gimplify_seq_add_stmt (pre_p, call);
 				}
 
 			      // mapped_arg = omp_get_mapped_ptr (arg,
-			      // device_num)
+			      // 		device_num)
 			      tree fn = builtin_decl_explicit (
 				BUILT_IN_OMP_GET_MAPPED_PTR);
 			      gimplify_arg (arg_p, pre_p, loc);
-			      gimplify_arg (&device_num, pre_p, loc);
-			      call
-				= gimple_build_call (fn, 2, *arg_p, device_num);
+			      gimplify_arg (&dispatch_device_num, pre_p, loc);
+			      call = gimple_build_call (fn, 2, *arg_p,
+							dispatch_device_num);
 			      tree mapped_arg = create_tmp_var (
 				gimple_call_return_type (call));
 			      gimple_call_set_lhs (call, mapped_arg);
diff --git a/gcc/testsuite/c-c++-common/gomp/append-args-1.c b/gcc/testsuite/c-c++-common/gomp/append-args-1.c
new file mode 100644
index 00000000000..ccd27947465
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/append-args-1.c
@@ -0,0 +1,85 @@
+/* { dg-additional-options "-fdump-tree-original"  }  */
+
+/* The following definitions are in omp_lib, which cannot be included
+   in gcc/testsuite/  */
+
+#if __cplusplus >= 201103L
+# define __GOMP_UINTPTR_T_ENUM : __UINTPTR_TYPE__
+#else
+# define __GOMP_UINTPTR_T_ENUM
+#endif
+
+typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_interop_none = 0,
+  __omp_interop_t_max__ = __UINTPTR_MAX__
+} omp_interop_t;
+
+float repl0(short, short);
+#pragma omp declare variant(repl0) match(construct={dispatch}) append_args(interop(target), interop(targetsync))
+float base0();
+/* { dg-error "argument 1 of 'repl0' must be of 'omp_interop_t'" "" { target c } .-3 }  */
+/* { dg-error "argument 1 of 'float repl0\\(short int, short int\\)' must be of 'omp_interop_t'" "" { target c++ } .-4 }  */
+/* { dg-note "'append_args' specified here" "" { target *-*-* } .-4 } */
+
+float repl1(omp_interop_t, omp_interop_t);
+#pragma omp declare variant(repl1) match(construct={dispatch}) append_args(interop(target), interop(targetsync))
+float base1();
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'repl1'" "" { target c } .-2 }  */
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'float repl1\\(omp_interop_t, omp_interop_t\\)'" "" { target c++ } .-3 }  */
+
+void repl2(int *, int *, omp_interop_t, omp_interop_t);
+#pragma omp declare variant(repl2) match(construct={dispatch}) adjust_args(need_device_ptr : y) \
+        append_args(interop(target, targetsync, prefer_type(1)), \
+                    interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")})))
+void base2(int *x, int *y);
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'repl2'" "" { target c } .-3 }  */
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl2\\(int\\*, int\\*, omp_interop_t, omp_interop_t\\)'" "" { target c++ } .-4 }  */
+
+void repl3(int, omp_interop_t, ...);
+#pragma omp declare variant(repl3) match(construct={dispatch}) \
+        append_args(interop(prefer_type("cuda", "hsa")))
+void base3(int, ...);
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'repl3'" "" { target c } .-2 }  */
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl3\\(int, omp_interop_t, \\.\\.\\.\\)'" "" { target c++ } .-3 }  */
+/* { dg-note "'declare variant' candidate 'repl3' declared here" "" { target c } .-4 } */
+/* { dg-note "'declare variant' candidate 'void repl3\\(int, omp_interop_t, \\.\\.\\.\\)' declared here" "" { target c++ } .-5 } */
+
+float repl4(short, short, omp_interop_t, short);
+#pragma omp declare variant(repl4) match(construct={dispatch}) append_args(interop(target)) append_args(interop(targetsync))  /* { dg-error "too many 'append_args' clauses" } */
+float base4(short, short);
+/* { dg-error "argument 4 of 'repl4' must be of 'omp_interop_t'" "" { target c } .-3 }  */
+/* { dg-error "argument 4 of 'float repl4\\(short int, short int, omp_interop_t, short int\\)' must be of 'omp_interop_t'" "" { target c++ } .-4 }  */
+/* { dg-note "'append_args' specified here" "" { target *-*-* } .-4 } */
+
+
+float
+test (int *a, int *b)
+{
+  omp_interop_t obj1, obj2;
+  float x;
+
+  #pragma omp dispatch interop ( obj1 )
+    x = base1 ();
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop ( obj1 )
+    base2 (a, b);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch
+    base3 (5, 1, 2, 3);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop (obj2)
+    base3 (5, 1, 2, 3);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop (obj2, obj1)
+    base3 (5, 1, 2, 3);
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(1\\) for 'declare variant' candidate 'repl3'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(1\\) for 'declare variant' candidate 'void repl3\\(int, omp_interop_t, \\.\\.\\.\\)'" "" { target c++ } .-3 } */
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-4 }  */
+
+  return x;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/append-args-2.c b/gcc/testsuite/c-c++-common/gomp/append-args-2.c
new file mode 100644
index 00000000000..8f2cad7210d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/append-args-2.c
@@ -0,0 +1,10 @@
+/* { dg-additional-options "-fdump-tree-original"  }  */
+
+/* omp_interop_t undefined (on purpose).  */
+
+float repl0(short, short);
+#pragma omp declare variant(repl0) match(construct={dispatch}) append_args(interop(target), interop(targetsync))
+float base0();
+/* { dg-error "argument 1 of 'repl0' must be of 'omp_interop_t'" "" { target c } .-3 }  */
+/* { dg-error "argument 1 of 'repl0' must be of 'omp_interop_t'" "" { target c++ } .-4 }  */
+/* { dg-note "'append_args' specified here" "" { target *-*-* } .-4 } */
diff --git a/gcc/testsuite/c-c++-common/gomp/append-args-3.c b/gcc/testsuite/c-c++-common/gomp/append-args-3.c
new file mode 100644
index 00000000000..24d9f69b561
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/append-args-3.c
@@ -0,0 +1,57 @@
+/* { dg-additional-options "-fdump-tree-original=gimple -Wall" }  */
+
+#if __cplusplus >= 201103L
+# define __GOMP_UINTPTR_T_ENUM : __UINTPTR_TYPE__
+#else
+# define __GOMP_UINTPTR_T_ENUM
+#endif
+
+typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_interop_none = 0,
+  __omp_interop_t_max__ = __UINTPTR_MAX__
+} omp_interop_t;
+
+
+void g(int, const char *, omp_interop_t, omp_interop_t);
+#pragma omp declare variant(g) match(construct={dispatch}) append_args(interop(target),interop(targetsync))
+void f(int x, const char *y);
+
+void foo()
+{
+  omp_interop_t obj1;  /* { dg-note "'obj1' was declared here" }  */
+  omp_interop_t obj2 = omp_interop_none;
+  #pragma omp dispatch device(9) novariants(1)
+     f(2, "abc");
+  #pragma omp dispatch device(5) interop(obj1,obj2)
+     f(3, "cde");  /* { dg-warning "'obj1' is used uninitialized \\\[-Wuninitialized\\\]" }  */
+}
+
+
+void varvar(int, int, omp_interop_t, omp_interop_t, ...);
+#pragma omp declare variant(varvar) match(construct={dispatch}) append_args(interop(target),interop(targetsync))
+void varbase(int x, int y, ...);
+
+void bar()
+{
+  omp_interop_t obj3 = omp_interop_none;
+  omp_interop_t obj4;  /* { dg-note "'obj4' was declared here" } */
+  #pragma omp dispatch device(3) nocontext(1)
+     varbase(10, 11, 101, 202, 303);
+  #pragma omp dispatch device(7) interop(obj3,obj4)
+     varbase(20, 21, 111, 222, 333);  /* { dg-warning "'obj4' is used uninitialized \\\[-Wuninitialized\\\]" }  */
+}
+
+/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 4 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(9\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(5\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(3\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(7\\);" 1 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(D\.\[0-9\]+\\);" 4 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "f \\(2, \"abc\"\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "g \\(3, \"cde\", obj1, obj2\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "varbase \\(10, 11, 101, 202, 303\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "varvar \\(20, 21, obj3, obj4, 111, 222, 333\\);" 1 "gimple" } }  */
diff --git a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
index 9f209f4ea66..026998e92c5 100644
--- a/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/declare-variant-2.c
@@ -8,9 +8,9 @@ void f3 (void);
 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' clause before end of line" } */
+#pragma omp declare variant (f1)	/* { dg-error "expected 'match', 'adjust_args' or 'append_args' clause before end of line" } */
 void f6 (void);
-#pragma omp declare variant (f1) simd	/* { dg-error "expected 'match' clause before 'simd'" } */
+#pragma omp declare variant (f1) simd	/* { dg-error "expected 'match', 'adjust_args' or 'append_args' clause before 'simd'" } */
 void f7 (void);
 #pragma omp declare variant (f1) match	/* { dg-error "expected '\\(' before end of line" } */
 void f8 (void);
diff --git a/gcc/testsuite/c-c++-common/gomp/dispatch-11.c b/gcc/testsuite/c-c++-common/gomp/dispatch-11.c
index 7f1d8062f25..e2372eb3cab 100644
--- a/gcc/testsuite/c-c++-common/gomp/dispatch-11.c
+++ b/gcc/testsuite/c-c++-common/gomp/dispatch-11.c
@@ -24,6 +24,10 @@ void repl2(int *, int *);
 #pragma omp declare variant(repl2) match(construct={dispatch}) adjust_args(need_device_ptr : y)
 void base2(int *x, int *y);
 
+void repl3(int *, int *, omp_interop_t);
+#pragma omp declare variant(repl3) match(construct={dispatch}) adjust_args(need_device_ptr : y) append_args(interop(target))
+void base3(int *x, int *y);
+
 
 float
 dupl (int *a, int *b)
@@ -33,47 +37,69 @@ dupl (int *a, int *b)
 
   #pragma omp dispatch interop ( obj1 ) interop(obj2)  /* { dg-error "too many 'interop' clauses" }  */
     x = base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'float repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'float repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop ( obj1) nocontext(1) interop (obj2 )  /* { dg-error "too many 'interop' clauses" }  */
     base2 (a, b);
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'base2'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void base2\\(int\\*, int\\*\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'base2'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void base2\\(int\\*, int\\*\\)'" "" { target c++ } .-3 } */
   return x;
 }
 
+/* { dg-note "'declare variant' candidate 'repl1' declared here" ""             { target c }   19 } */
+/* { dg-note "'declare variant' candidate 'float repl1\\(\\)' declared here" "" { target c++ } 19 } */
+/* { dg-note "'declare variant' candidate 'base1' declared here" ""             { target c }   21 } */
+/* { dg-note "'declare variant' candidate 'float base1\\(\\)' declared here" "" { target c++ } 21 } */
+/* { dg-note "'declare variant' candidate 'repl2' declared here" ""                          { target c }   23 } */
+/* { dg-note "'declare variant' candidate 'void repl2\\(int\\*, int\\*\\)' declared here" "" { target c++ } 23 } */
+/* { dg-note "'declare variant' candidate 'base2' declared here" ""                          { target c }   25 } */
+/* { dg-note "'declare variant' candidate 'void base2\\(int\\*, int\\*\\)' declared here" "" { target c++ } 25 } */
+/* { dg-note "'declare variant' candidate 'repl3' declared here" ""                                         { target c }   28 } */
+/* { dg-note "'declare variant' candidate 'void repl3\\(int\\*, int\\*, omp_interop_t\\)' declared here" "" { target c++ } 28 } */
 
 float
 test (int *a, int *b)
 {
-  omp_interop_t obj1, obj2;
+  omp_interop_t obj1, obj2, obj3;
   float x, y;
 
   #pragma omp dispatch interop ( obj1 )
     x = base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'float repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'float repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop ( obj1, obj1 )  /* Twice the same - should be fine.  */
     x = base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'float repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'float repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch novariants(1) interop(obj2, obj1)
     y = base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'base1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'float base1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'base1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'float base1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop(obj2, obj1)
     base2 (a, b);
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl2'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void repl2\\(int\\*, int\\*\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl2'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void repl2\\(int\\*, int\\*\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop(obj2) nocontext(1)
     base2 (a, b);
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'base2'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void base2\\(int\\*, int\\*\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'base2'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void base2\\(int\\*, int\\*\\)'" "" { target c++ } .-3 } */
+
+  #pragma omp dispatch interop(obj3, obj2)
+    base3 (a, b);
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(1\\) for 'declare variant' candidate 'repl3'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(2\\) exceeds the number of 'append_args' items \\(1\\) for 'declare variant' candidate 'void repl3\\(int\\*, int\\*, omp_interop_t\\)'" "" { target c++ } .-3 } */
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-4 } */
+
+  #pragma omp dispatch interop(obj3)
+    base3 (a, b);
+  /* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'repl3'" "" { target c } 28 }  */
+  /* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl3\\(int\\*, int\\*, omp_interop_t\\)'" "" { target c++ } 28 }  */
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-4 } */
   return x + y;
 }
 
@@ -82,3 +108,5 @@ test (int *a, int *b)
 /* { dg-final { scan-tree-dump-times "#pragma omp dispatch interop\\(obj1\\) interop\\(obj2\\) novariants\\(1\\)\[\\n\\r\]" 1 "original" } } */
 /* { dg-final { scan-tree-dump-times "#pragma omp dispatch interop\\(obj1\\) interop\\(obj2\\)\[\\n\\r\]" 1 "original" } } */
 /* { dg-final { scan-tree-dump-times "#pragma omp dispatch nocontext\\(1\\) interop\\(obj2\\)\[\\n\\r\]" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp dispatch interop\\(obj2\\) interop\\(obj3\\)\[\\n\\r\]" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp dispatch interop\\(obj3\\)\[\\n\\r\]" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/dispatch-12.c b/gcc/testsuite/c-c++-common/gomp/dispatch-12.c
index ea190c74e84..0b8fdfab5ca 100644
--- a/gcc/testsuite/c-c++-common/gomp/dispatch-12.c
+++ b/gcc/testsuite/c-c++-common/gomp/dispatch-12.c
@@ -28,26 +28,26 @@ test ()
 
   #pragma omp dispatch interop ( obj1, obj2, obj1 )  /* { dg-error "'obj2' must be of 'omp_interop_t'" }  */
     base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(3\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(3\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop ( obj3 )  /* { dg-error "'obj3' must be of 'omp_interop_t'" }  */
     base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop ( obj1 )
     base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop ( obj2 )  /* { dg-error "'obj2' must be of 'omp_interop_t'" }  */
     base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
 
   #pragma omp dispatch interop ( x )  /* { dg-error "'x' must be of 'omp_interop_t'" }  */
     base1 ();
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
-  /* { dg-error "number of list items in 'interop' clause exceeds number of 'append_args' items for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'repl1'" "" { target c } .-2 } */
+  /* { dg-error "number of list items in 'interop' clause \\(1\\) exceeds the number of 'append_args' items \\(0\\) for 'declare variant' candidate 'void repl1\\(\\)'" "" { target c++ } .-3 } */
 }
diff --git a/gcc/testsuite/g++.dg/gomp/append-args-1.C b/gcc/testsuite/g++.dg/gomp/append-args-1.C
new file mode 100644
index 00000000000..42cc58f0cb8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/append-args-1.C
@@ -0,0 +1,136 @@
+/* { dg-additional-options "-fdump-tree-original"  }  */
+
+/* The following definitions are in omp_lib, which cannot be included
+gcc/testsuite/g++.dg/gomp/append-args-1.C   in gcc/testsuite/  */
+
+#if __cplusplus >= 201103L
+# define __GOMP_UINTPTR_T_ENUM : __UINTPTR_TYPE__
+#else
+# define __GOMP_UINTPTR_T_ENUM
+#endif
+
+typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_interop_none = 0,
+  __omp_interop_t_max__ = __UINTPTR_MAX__
+} omp_interop_t;
+
+template<typename T, typename T2>
+float repl1(T, T2, T2);
+#pragma omp declare variant(repl1) match(construct={dispatch}) append_args(interop(target,prefer_type(1,5,4)), interop(targetsync))
+template<typename T>
+float base1(T);
+
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'float repl1\\(T, T2, T2\\) \\\[with T = short int; T2 = omp_interop_t\\\]'" "" { target *-*-* } .-4 }  */
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'float repl1\\(T, T2, T2\\) \\\[with T = omp_interop_t; T2 = omp_interop_t\\\]'" "" { target *-*-* } .-5 }  */
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'float repl1\\(T, T2, T2\\) \\\[with T = float; T2 = omp_interop_t\\\]'" "" { target *-*-* } .-6 }  */
+
+
+
+template<typename T, typename T2, typename T3>
+void repl3inval(T, T2, float);
+#pragma omp declare variant(repl3inval) match(construct={dispatch}) adjust_args(nothing : y) \
+        append_args(interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")}),target,targetsync))
+template<typename T, typename T2>
+void base2inval(T x, T2 y);
+
+/* { dg-error "no matching function for call to 'repl3inval\\(int\\*, omp_interop_t, omp_interop_t\\)'" "" { target *-*-* } .-5 }  */
+/* { dg-note "there is 1 candidate" "" { target *-*-* } .-6 }  */
+/* { dg-note "candidate 1: 'template<class T, class T2, class T3> void repl3inval\\(T, T2, float\\)'" "" { target *-*-* } .-8 }  */
+/* { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-9 }  */
+/* { dg-note "couldn't deduce template parameter 'T3'" "" { target *-*-* } .-9 }  */
+
+
+template<typename T>
+void repl99(T);
+#pragma omp declare variant(repl99) match(construct={dispatch}) \
+        append_args(interop(target, targetsync, prefer_type("cuda")))
+void base99();
+
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl99\\(T\\) \\\[with T = omp_interop_t\\\]'" "" { target *-*-* } .-3 }  */
+
+
+
+template<typename T, typename T2, typename T3>
+void repl2(T, T2, T3, T3);
+#pragma omp declare variant(repl2) match(construct={dispatch}) adjust_args(need_device_ptr : y) \
+        append_args(interop(target, targetsync, prefer_type(1)), \
+                    interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")})))
+template<typename T, typename T2>
+void base2(T x, T2 y);
+
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl2\\(T, T2, T3, T3\\) \\\[with T = int\\*; T2 = int\\*; T3 = omp_interop_t\\\]'" "" { target *-*-* } .-5 }  */
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl2\\(T, T2, T3, T3\\) \\\[with T = int\\*; T2 = omp_interop_t; T3 = omp_interop_t\\\]'" "" { target *-*-* } .-6 }  */
+
+
+template<typename T,typename T3>
+void tooFewRepl(T, T, T3);
+#pragma omp declare variant(tooFewRepl) match(construct={dispatch}) \
+        append_args(interop(target, targetsync, prefer_type(1)), \
+                    interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")})))
+template<typename T, typename T2>
+void tooFewBase(T x, T2 y);
+
+/* { dg-error "no matching function for call to 'tooFewRepl\\(int\\*, int\\*, omp_interop_t, omp_interop_t\\)'" "" { target *-*-* } .-6 }  */
+/* { dg-note "there is 1 candidate" "" { target *-*-* } .-7 }  */
+/* { dg-note "candidate 1: 'template<class T, class T3> void tooFewRepl\\(T, T, T3\\)'" "" { target *-*-* } .-9 }  */
+/* { dg-note "candidate expects 3 arguments, 4 provided" "" { target *-*-* } .-10 }  */
+
+
+
+template<typename T, typename T2>
+void repl3(T, T2, ...);
+#pragma omp declare variant(repl3) match(construct={dispatch}) \
+        append_args(interop(prefer_type("cuda", "hsa")))
+template<typename T>
+void base3(T, ...);
+
+/* { dg-message "sorry, unimplemented: 'append_args' clause not yet supported for 'void repl3\\(T, T2, \.\.\.\\) \\\[with T = int\\*; T2 = omp_interop_t\\\]'" "" { target *-*-* } .-4 }  */
+
+
+
+float
+test (int *a, int *b)
+{
+  omp_interop_t obj1, obj2;
+  float x, y;
+
+  #pragma omp dispatch interop ( obj1, obj2 )
+    x = base1<short> (5);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch
+    base2inval<int *, omp_interop_t> (a, omp_interop_none);
+
+  #pragma omp dispatch
+    base99 ();
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop ( obj1 )
+    base2<int *, omp_interop_t> (b, omp_interop_none);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop ( obj1 )
+    base2<int *, int *> (b, a);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop ( obj1 )
+    x = base1<omp_interop_t> (omp_interop_none);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch interop ( obj1 )
+    x = base1<float> (1.0f);
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  #pragma omp dispatch
+    tooFewBase<int*,int*>(a,b);
+
+  #pragma omp dispatch nocontext(1)
+    base3<int*>(a, 1, 2, "abc");
+
+  #pragma omp dispatch
+    base3<int*>(a, 1, 2, "abc");
+  /* { dg-note "required by 'dispatch' construct" "" { target *-*-* } .-2 }  */
+
+  return x;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/append-args-2.C b/gcc/testsuite/g++.dg/gomp/append-args-2.C
new file mode 100644
index 00000000000..5092d562a8e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/append-args-2.C
@@ -0,0 +1,55 @@
+/* { dg-additional-options "-fdump-tree-original"  }  */
+
+/* omp_interop_t is undefined (on purpose)  */
+
+
+template<typename T, typename T2>
+float repl1(T, T2, T2);  /* { dg-error "argument 2 of 'repl1' must be of 'omp_interop_t'" }  */
+#pragma omp declare variant(repl1) match(construct={dispatch}) append_args(interop(target,prefer_type(1,5,4)), interop(targetsync))  /* { dg-note "'append_args' specified here" }  */
+template<typename T>
+float base1(T);
+
+
+
+template<typename T, typename T2, typename T3>
+void repl3inval(T, T2, float);  /* { dg-error "argument 3 of 'repl3inval' must be of 'omp_interop_t'" }  */
+#pragma omp declare variant(repl3inval) match(construct={dispatch}) adjust_args(nothing : y) \
+        append_args(interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")}),target,targetsync))  /* { dg-note "'append_args' specified here" }  */
+template<typename T, typename T2>
+void base2inval(T x, T2 y);
+
+
+
+template<typename T>
+void repl99(T);  /* { dg-error "argument 1 of 'repl99' must be of 'omp_interop_t'" }  */
+#pragma omp declare variant(repl99) match(construct={dispatch}) \
+        append_args(interop(target, targetsync, prefer_type("cuda")))  /* { dg-note "'append_args' specified here" }  */
+void base99();
+
+
+
+template<typename T, typename T2, typename T3>
+void repl2(T, T2, T3, T3);  /* { dg-error "argument 3 of 'repl2' must be of 'omp_interop_t'" }  */
+#pragma omp declare variant(repl2) match(construct={dispatch}) adjust_args(need_device_ptr : y) \
+        append_args(interop(target, targetsync, prefer_type(1)),  /* { dg-note "'append_args' specified here" }  */ \
+                    interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")})))
+template<typename T, typename T2>
+void base2(T x, T2 y);
+
+
+template<typename T,typename T3>
+void tooFewRepl(T, T, T3);  /* { dg-error "argument 3 of 'tooFewRepl' must be of 'omp_interop_t'" }  */
+#pragma omp declare variant(tooFewRepl) match(construct={dispatch}) \
+        append_args(interop(target, targetsync, prefer_type(1)),  /* { dg-note "'append_args' specified here" }  */ \
+                    interop(prefer_type({fr(3), attr("ompx_nop")},{fr(2)},{attr("ompx_all")})))
+template<typename T, typename T2>
+void tooFewBase(T x, T2 y);
+
+
+
+template<typename T, typename T2>
+void repl3(T, T2, ...);  /* { dg-error "argument 2 of 'repl3' must be of 'omp_interop_t'" }  */
+#pragma omp declare variant(repl3) match(construct={dispatch}) \
+        append_args(interop(prefer_type("cuda", "hsa")))  /* { dg-note "'append_args' specified here" }  */
+template<typename T>
+void base3(T, ...);
diff --git a/gcc/testsuite/g++.dg/gomp/append-args-3.C b/gcc/testsuite/g++.dg/gomp/append-args-3.C
new file mode 100644
index 00000000000..62f8b40f55f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/append-args-3.C
@@ -0,0 +1,100 @@
+/* { dg-additional-options "-fdump-tree-original=gimple -Wall" }  */
+
+#if __cplusplus >= 201103L
+# define __GOMP_UINTPTR_T_ENUM : __UINTPTR_TYPE__
+#else
+# define __GOMP_UINTPTR_T_ENUM
+#endif
+
+typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_interop_none = 0,
+  __omp_interop_t_max__ = __UINTPTR_MAX__
+} omp_interop_t;
+
+
+template<typename T, typename T2, typename T3>
+void g(T, T2, T3, T3);
+#pragma omp declare variant(g) match(construct={dispatch}) append_args(interop(target),interop(targetsync))
+template<typename T, typename T2>
+void f(T x, T2 y);
+
+void foo()
+{
+  omp_interop_t obj1;  /* { dg-note "'obj1' was declared here" }  */
+  omp_interop_t obj2 = omp_interop_none;
+  #pragma omp dispatch device(9) novariants(1)
+     f<int, const char *>(2, (const char*)"abc");
+  #pragma omp dispatch device(5) interop(obj1,obj2)
+     f<int, const char *>(3, (const char*)"cde");  /* { dg-warning "'obj1' is used uninitialized \\\[-Wuninitialized\\\]" }  */
+
+  #pragma omp dispatch device(9) novariants(1)
+     f<int, omp_interop_t>(2, omp_interop_none);
+  #pragma omp dispatch device(5) interop(obj1,obj2)
+     f<int, omp_interop_t>(3, omp_interop_none);
+}
+
+
+template<typename Ta, typename Tb, typename Tc>
+void varvar(Ta, Tb, Tc, Tc, ...);
+#pragma omp declare variant(varvar) match(construct={dispatch}) append_args(interop(target),interop(targetsync))
+template<typename Ta, typename Tb>
+void varbase(Ta x, Tb y, ...);
+
+void bar()
+{
+  omp_interop_t obj3 = omp_interop_none;
+  omp_interop_t obj4;  /* { dg-note "'obj4' was declared here" } */
+  #pragma omp dispatch device(3) nocontext(1)
+     varbase<int, int>(10, 11, 101, 202, 303);
+  #pragma omp dispatch device(7) interop(obj3,obj4)
+     varbase<int, int>(20, 21, 111, 222, 333);  /* { dg-warning "'obj4' is used uninitialized \\\[-Wuninitialized\\\]" }  */
+
+  #pragma omp dispatch device(3) nocontext(1)
+     varbase<int, omp_interop_t>(10, omp_interop_none, 101, 202, 303);
+  #pragma omp dispatch device(7) interop(obj3,obj4)
+     varbase<int, int>(20, omp_interop_none, 111, 222, 333);
+}
+
+
+
+template<typename T>
+void nargVar(T, T);
+#pragma omp declare variant(nargVar) match(construct={dispatch}) append_args(interop(target),interop(targetsync))
+void nargsBase();
+
+void foobar()
+{
+  omp_interop_t obj5 = omp_interop_none;
+  omp_interop_t obj6 = omp_interop_none;
+  #pragma omp dispatch device(1) nocontext(1)
+     nargsBase();
+  #pragma omp dispatch device(2) interop(obj5,obj6)
+     nargsBase();
+}
+
+/* { dg-final { scan-tree-dump-times "D\.\[0-9\]+ = __builtin_omp_get_default_device \\(\\);" 10 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(9\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(5\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(3\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(7\\);" 2 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(1\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(2\\);" 1 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "__builtin_omp_set_default_device \\(D\.\[0-9\]+\\);" 10 "gimple" } }  */
+
+
+/* { dg-final { scan-tree-dump-times "f <int, const\*> \\(2, \"abc\"\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "g <int, const\*, omp_interop_t> \\(3, \"cde\", obj1, obj2\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "f <int, omp_interop_t> \\(2, 0\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "g <int, omp_interop_t, omp_interop_t> \\(3, 0, obj1, obj2\\);" 1 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "varbase<int, int> \\(10, 11, 101, 202, 303\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "varvar<int, int, omp_interop_t> \\(20, 21, obj3, obj4, 111, 222, 333\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "varbase<int, omp_interop_t> \\(10, 0, 101, 202, 303\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "varvar<int, omp_interop_t, omp_interop_t> \\(20, 0, obj3, obj4, 111, 222, 333\\);" 1 "gimple" } }  */
+
+/* { dg-final { scan-tree-dump-times "nargsBase \\(\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "nargVar<omp_interop_t> \\(obj5, obj6\\);" 1 "gimple" } }  */

Reply via email to