This patch implements C++ support for the "begin declare variant"
construct.  The OpenMP specification is hazy on interaction of this
feature with C++ language features.  Variant functions in classes are
supported but must be defined as members in the class definition,
using an unqualified name for the base function which also must be
present in that class.  Similarly variant functions in a namespace can
only be defined in that namespace using an unqualified name for a base
function already declared in that namespace.  Variants for template
functions or inside template classes seem to (mostly) work.

gcc/cp/ChangeLog
        * cp-tree.h (struct cp_omp_declare_variant_attr): New.
        (struct saved_scope): Add omp_declare_variant_attribute field.
        * decl.cc (omp_declare_variant_finalize_one): Add logic to inject
        "this" parameter for method calls.
        * parser.cc (cp_parser_skip_to_pragma_omp_end_declare_variant): New.
        (omp_start_variant_function): New.
        (omp_finish_variant_function): New.
        (cp_parser_init_declarator): Handle variant functions.
        (cp_parser_class_specifier): Handle deferred lookup of base functions
        when the entire class has been seen.
        (cp_parser_member_declaration): Handle variant functions.
        (cp_finish_omp_declare_variant): Merge context selectors if in
        a "begin declare variant" block.
        (cp_parser_omp_begin): Match "omp begin declare variant".  Adjust
        error messages.
        (cp_parser_omp_end): Match "omp end declare variant".
        * parser.h (struct cp_parser): Add omp_unregistered_variants field.
        * semantics.cc (finish_translation_unit): Detect unmatched
        "omp begin declare variant".

gcc/testsuite/ChangeLog
        * g++.dg/gomp/delim-declare-variant-1.C: New.
        * g++.dg/gomp/delim-declare-variant-2.C: New.
        * g++.dg/gomp/delim-declare-variant-3.C: New.
        * g++.dg/gomp/delim-declare-variant-4.C: New.
        * g++.dg/gomp/delim-declare-variant-5.C: New.
        * g++.dg/gomp/delim-declare-variant-6.C: New.
        * g++.dg/gomp/delim-declare-variant-7.C: New.
        * g++.dg/gomp/delim-declare-variant-40.C: New.
        * g++.dg/gomp/delim-declare-variant-41.C: New.
        * g++.dg/gomp/delim-declare-variant-50.C: New.
        * g++.dg/gomp/delim-declare-variant-51.C: New.
        * g++.dg/gomp/delim-declare-variant-52.C: New.
        * g++.dg/gomp/delim-declare-variant-70.C: New.
        * g++.dg/gomp/delim-declare-variant-71.C: New.

libgomp/
        * testsuite/libgomp.c++/delim-declare-variant-1.C: New.
        * testsuite/libgomp.c++/delim-declare-variant-2.C: New.
        * libgomp/testsuite/libgomp.c++/delim-declare-variant-7.C: New.

Co-Authored-By: Julian Brown <jul...@codesourcery.com>
Co-Authored-By: waffl3x <waff...@baylibre.com>
---
 gcc/cp/cp-tree.h                              |   6 +
 gcc/cp/decl.cc                                |  15 +
 gcc/cp/parser.cc                              | 544 ++++++++++++++++--
 gcc/cp/parser.h                               |   5 +
 gcc/cp/semantics.cc                           |   7 +
 .../g++.dg/gomp/delim-declare-variant-1.C     |  39 ++
 .../g++.dg/gomp/delim-declare-variant-2.C     |  53 ++
 .../g++.dg/gomp/delim-declare-variant-3.C     |  37 ++
 .../g++.dg/gomp/delim-declare-variant-4.C     |  57 ++
 .../g++.dg/gomp/delim-declare-variant-40.C    |  51 ++
 .../g++.dg/gomp/delim-declare-variant-41.C    |  31 +
 .../g++.dg/gomp/delim-declare-variant-5.C     |  53 ++
 .../g++.dg/gomp/delim-declare-variant-50.C    |  99 ++++
 .../g++.dg/gomp/delim-declare-variant-51.C    | 181 ++++++
 .../g++.dg/gomp/delim-declare-variant-52.C    |  24 +
 .../g++.dg/gomp/delim-declare-variant-6.C     |  72 +++
 .../g++.dg/gomp/delim-declare-variant-7.C     |  57 ++
 .../g++.dg/gomp/delim-declare-variant-70.C    | 206 +++++++
 .../g++.dg/gomp/delim-declare-variant-71.C    | 157 +++++
 .../libgomp.c++/delim-declare-variant-1.C     |  29 +
 .../libgomp.c++/delim-declare-variant-2.C     |  37 ++
 .../libgomp.c++/delim-declare-variant-7.C     |  39 ++
 22 files changed, 1766 insertions(+), 33 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C
 create mode 100644 libgomp/testsuite/libgomp.c++/delim-declare-variant-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/delim-declare-variant-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/delim-declare-variant-7.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ec976928f5f..d5efca834f5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1922,6 +1922,11 @@ struct GTY(()) cp_omp_begin_assumes_data {
   bool attr_syntax;
 };
 
+struct GTY(()) cp_omp_declare_variant_attr {
+  bool attr_syntax;
+  tree selector;
+};
+
 /* Global state.  */
 
 struct GTY(()) saved_scope {
@@ -1972,6 +1977,7 @@ struct GTY(()) saved_scope {
   hash_map<tree, tree> *GTY((skip)) x_local_specializations;
   vec<cp_omp_declare_target_attr, va_gc> *omp_declare_target_attribute;
   vec<cp_omp_begin_assumes_data, va_gc> *omp_begin_assumes;
+  vec<cp_omp_declare_variant_attr, va_gc> *omp_declare_variant_attribute;
 
   struct saved_scope *prev;
 };
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 84abc17ade0..63cd90de0f7 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8545,6 +8545,21 @@ omp_declare_variant_finalize_one (tree decl, tree attr)
   if (idk == CP_ID_KIND_QUALIFIED)
     variant = finish_call_expr (variant, &args, /*disallow_virtual=*/true,
                                koenig_p, tf_warning_or_error);
+  else if (idk == CP_ID_KIND_NONE
+          && TREE_CODE (variant) == FUNCTION_DECL
+          && DECL_IOBJ_MEMBER_FUNCTION_P (variant)
+          && CLASS_TYPE_P (DECL_CONTEXT (decl)))
+    {
+      tree saved_ccp = current_class_ptr;
+      tree saved_ccr = current_class_ref;
+      current_class_ptr = NULL_TREE;
+      current_class_ref = NULL_TREE;
+      inject_this_parameter (DECL_CONTEXT (decl), TYPE_UNQUALIFIED);
+      variant = finish_call_expr (variant, &args, /*disallow_virtual=*/false,
+                                 koenig_p, tf_warning_or_error);
+      current_class_ptr = saved_ccp;
+      current_class_ref = saved_ccr;
+    }
   else
     variant = finish_call_expr (variant, &args, /*disallow_virtual=*/false,
                                koenig_p, tf_warning_or_error);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 9d0970b4d83..35a7b32789d 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -4524,6 +4524,54 @@ cp_parser_require_pragma_eol (cp_parser *parser, 
cp_token *pragma_tok)
     }
 }
 
+/* Skip tokens up to and including "#pragma omp end declare variant".
+   Properly handle nested "#pragma omp begin declare variant" pragmas.  */
+static void
+cp_parser_skip_to_pragma_omp_end_declare_variant (cp_parser *parser)
+{
+  for (int depth = 0; depth >= 0; )
+    {
+      cp_token *token = cp_lexer_peek_token (parser->lexer);
+
+      switch (token->type)
+       {
+       case CPP_PRAGMA_EOL:
+         if (!parser->lexer->in_pragma)
+           break;
+         /* FALLTHRU */
+       case CPP_EOF:
+         /* If we've run out of tokens, stop.  */
+         return;
+
+       case CPP_PRAGMA:
+         if ((cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN
+              || cp_parser_pragma_kind (token) == PRAGMA_OMP_END)
+             && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)
+             && cp_lexer_nth_token_is (parser->lexer, 3, CPP_NAME))
+           {
+             tree id1 = cp_lexer_peek_nth_token (parser->lexer, 2)->u.value;
+             tree id2 = cp_lexer_peek_nth_token (parser->lexer, 3)->u.value;
+             if (strcmp (IDENTIFIER_POINTER (id1), "declare") == 0
+                 && strcmp (IDENTIFIER_POINTER (id2), "variant") == 0)
+               {
+                 if (cp_parser_pragma_kind (token) == PRAGMA_OMP_BEGIN)
+                   depth++;
+                 else
+                   depth--;
+               }
+           }
+         cp_parser_skip_to_pragma_eol (parser, token);
+         continue;
+
+       default:
+         break;
+       }
+
+      /* Consume the token.  */
+      cp_lexer_consume_token (parser->lexer);
+    }
+}
+
 /* This is a simple wrapper around make_typename_type. When the id is
    an unresolved identifier node, we can provide a superior diagnostic
    using cp_parser_diagnose_invalid_type_name.  */
@@ -23770,6 +23818,226 @@ cp_parser_maybe_adjust_declarator_for_dguide 
(cp_parser *parser,
     }
 }
 
+/* Helper function for OpenMP "begin declare variant" directives.
+   Function definitions inside the construct need to have their names
+   mangled according to the context selector CTX.  The DECLARATOR is
+   modified in place to point to a new identifier; the original name of
+   the function is returned.  */
+static tree
+omp_start_variant_function (cp_declarator *declarator, tree ctx)
+{
+  cp_declarator *id = get_id_declarator (declarator);
+  tree name = id->u.id.unqualified_name;
+  tree scope = id->u.id.qualifying_scope;
+  enum special_function_kind sfk = id->u.id.sfk;
+
+  /* There seems to be no reasonable interpretation of what the behavior
+     should be if the name is qualified.  You cannot add the variant function
+     to a class or namespace from outside of that scope.  */
+  if (scope)
+    {
+      sorry_at (id->id_loc,
+               "cannot handle qualified name for variant function");
+      return NULL_TREE;
+    }
+
+  /* Catch disallowed constructors and destructors now.  We can't mangle
+     destructor names (which are not IDENTIFIER_NODEs) in any case.  */
+  if (sfk == sfk_constructor)
+    {
+      error_at (id->id_loc,
+               "declare variant directives are not allowed on constructors");
+      return NULL_TREE;
+    }
+  if (sfk == sfk_destructor)
+    {
+      error_at (id->id_loc,
+               "declare variant directives are not allowed on destructors");
+      return NULL_TREE;
+    }
+  if (TREE_CODE (name) != IDENTIFIER_NODE)
+    {
+      sorry_at (id->id_loc,
+               "cannot handle %s identifier name",
+               get_tree_code_name (TREE_CODE (name)));
+      return NULL_TREE;
+    }
+
+  /* Mangle the name in the declarator.  */
+  id->u.id.unqualified_name
+    = omp_mangle_variant_name (name, ctx, JOIN_STR);
+
+  return name;
+}
+
+/* Helper function for OpenMP "begin declare variant" directives.  Now
+   that we have a DECL for the variant function, and BASE_NAME for the
+   base function, look up the decl for BASE_NAME in the same scope as
+   DECL, add an "omp declare variant base" attribute pointing at CTX
+   to the base decl, and an "omp declare variant variant" attribute to
+   the variant DECL.  */
+static void
+omp_finish_variant_function (cp_parser *parser, tree decl, tree base_name,
+                            tree ctx)
+{
+  tree match = NULL_TREE;
+  bool is_template = false;
+  tree decl_context = CP_DECL_CONTEXT (decl);
+
+  /* First find the base_decl.  */
+  tree base_decl = cp_parser_lookup_name_simple (parser, base_name,
+                                                DECL_SOURCE_LOCATION (decl));
+
+  if (base_decl == error_mark_node)
+    base_decl = NULL_TREE;
+  if (!base_decl)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "no previous declaration of base function in this scope");
+      return;
+    }
+
+  /* Find the right overloaded function.  */
+  if (TREE_CODE (base_decl) == OVERLOAD)
+    {
+      for (ovl_iterator iter (base_decl); iter; ++iter)
+       {
+         tree bb = *iter;
+         if (decls_match (decl, bb))
+           {
+             match = bb;
+             break;
+           }
+         else if (TREE_CODE (bb) == TEMPLATE_DECL
+                  && TREE_CODE (decl) == FUNCTION_DECL
+                  && DECL_TEMPLATE_INFO (decl))
+           {
+             tree decl_template = DECL_TI_TEMPLATE (decl);
+             if (decl_template
+                 && PRIMARY_TEMPLATE_P (decl_template)
+                 && decls_match (bb, decl_template))
+               {
+                 /* We want to put the attributes on the function rather
+                    than on the TEMPLATE_DECL that points to it.  */
+                 match = DECL_TEMPLATE_RESULT (bb);
+                 is_template = true;
+                 break;
+               }
+           }
+       }
+      }
+  else if (decls_match (decl, base_decl))
+    match = base_decl;
+  else if (TREE_CODE (base_decl) == TEMPLATE_DECL)
+    /* Per comment in cp-tree.h, TEMPLATE_DECLs are always wrapped in an
+       OVERLOAD, so we should never see them here.  */
+    gcc_unreachable ();
+  else if (TREE_CODE (base_decl) == TREE_LIST)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl), "base function is ambiguous");
+      return;
+    }
+  else if (TREE_CODE (base_decl) == SCOPE_REF)
+    {
+      /* This shows up in some cases involving templates; it's apparently a
+        placeholder for names that can't be matched to a declaration
+        until template instantiation.  */
+      sorry_at (DECL_SOURCE_LOCATION (decl),
+               "base function cannot be resolved");
+      return;
+    }
+
+  if (!match)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "variant function definition does not match previous "
+               "declaration of %qE", base_decl);
+      return;
+    }
+  else if (CP_DECL_CONTEXT (match) != decl_context)
+    {
+      /* Reject inherited or using decls.  */
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "variant function must be in the same scope as the "
+               "base function %qE", match);
+      return;
+    }
+  else if (DECL_VIRTUAL_P (decl) || DECL_VIRTUAL_P (match))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "declare variant directives are not allowed on "
+               "virtual functions");
+      return;
+    }
+  else if (DECL_DEFAULTED_FN (decl) || DECL_DEFAULTED_FN (match))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "declare variant directives are not allowed on "
+               "defaulted functions");
+      return;
+    }
+  else if (DECL_DELETED_FN (decl) || DECL_DELETED_FN (match))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "declare variant directives are not allowed on "
+               "deleted functions");
+      return;
+    }
+  else if (DECL_IMMEDIATE_FUNCTION_P (decl)
+          || DECL_IMMEDIATE_FUNCTION_P (match))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+               "declare variant directives are not allowed on "
+               "immediate functions");
+      return;
+    }
+
+  /* Inside a template, make the "omp declare variant base" attribute
+     point to the name of DECL rather than DECL itself.  During template
+     instantiation, omp_declare_variant_finalize_one will handle this
+     using the same logic as for the non-delimited form of "declare variant",
+     causing template instantiation as needed.  For the non-template case,
+     there is nothing that will trigger omp_declare_variant_finalize_one;
+     so we create the final form of the attribute here, which points
+     directly to DECL rather than its name.  */
+  tree decl_or_name = decl;
+  cp_id_kind idk = CP_ID_KIND_NONE;
+  if (processing_template_decl && is_template)
+    {
+      decl_or_name = DECL_NAME (decl);
+      idk = CP_ID_KIND_TEMPLATE_ID;
+    }
+
+  omp_check_for_duplicate_variant (DECL_SOURCE_LOCATION (decl),
+                                  match, ctx);
+  tree construct
+    = omp_get_context_selector_list (ctx, OMP_TRAIT_SET_CONSTRUCT);
+  omp_mark_declare_variant (DECL_SOURCE_LOCATION (decl), decl, construct);
+
+  tree attrs = DECL_ATTRIBUTES (match);
+  tree match_loc_node
+    = maybe_wrap_with_location (integer_zero_node,
+                               DECL_SOURCE_LOCATION (match));
+  tree loc_node = tree_cons (match_loc_node,
+                            build_int_cst (integer_type_node, idk),
+                            build_tree_list (match_loc_node,
+                                             integer_zero_node));
+  attrs = tree_cons (get_identifier ("omp declare variant base"),
+                    tree_cons (decl_or_name, ctx, loc_node), attrs);
+  if (processing_template_decl)
+    ATTR_IS_DEPENDENT (attrs) = 1;
+  DECL_ATTRIBUTES (match) = attrs;
+
+  /* Variant functions are essentially anonymous and cannot be
+     referenced by name, so make them have internal linkage.  Note
+     that class methods in C++ normally have external linkage with
+     weak/comdat semantics; this prevents that.  */
+  TREE_PUBLIC (decl) = 0;
+  DECL_COMDAT (decl) = 0;
+  DECL_INTERFACE_KNOWN (decl) = 1;
+  DECL_NOT_REALLY_EXTERN (decl) = 1;
+}
+
 /* Declarators [gram.dcl.decl] */
 
 /* Parse an init-declarator.
@@ -23986,6 +24254,27 @@ cp_parser_init_declarator (cp_parser* parser,
          /* This is a function-definition.  */
          *function_definition_p = true;
 
+         /* If we're in an OpenMP "begin declare variant" block, the
+            name in the declarator refers to the base function.  We need
+            to save that and modify the declarator to have the mangled
+            name for the variant function instead.  */
+         tree dv_base = NULL_TREE;
+         tree dv_ctx = NULL_TREE;
+         vec<cp_omp_declare_variant_attr, va_gc> *dv_state
+           = scope_chain->omp_declare_variant_attribute;
+
+         if (!vec_safe_is_empty (dv_state))
+           {
+             cp_omp_declare_variant_attr a = dv_state->last ();
+             dv_ctx = copy_list (a.selector);
+             dv_base = omp_start_variant_function (declarator, dv_ctx);
+             if (dv_base == NULL_TREE)
+               {
+                 cp_parser_skip_to_end_of_statement (parser);
+                 return error_mark_node;
+               }
+           }
+
          /* Parse the function definition.  */
          if (member_p)
            decl = cp_parser_save_member_function_body (parser,
@@ -24004,6 +24293,11 @@ cp_parser_init_declarator (cp_parser* parser,
                = func_brace_location;
            }
 
+         /* If this function was in a "begin declare variant" block,
+            store the pointer back to the base function and fix up
+            the attributes for the middle end.  */
+         if (dv_base && decl != error_mark_node)
+           omp_finish_variant_function (parser, decl, dv_base, dv_ctx);
          return decl;
        }
     }
@@ -24081,6 +24375,27 @@ cp_parser_init_declarator (cp_parser* parser,
            is_initialized = SD_DEFAULTED;
          else if (t2->keyword == RID_DELETE)
            is_initialized = SD_DELETED;
+         if (!vec_safe_is_empty (scope_chain->omp_declare_variant_attribute))
+           {
+             /* We're in a "begin declare variant" construct.  The parser
+                doesn't go through the normal function definition path for
+                these and hence doesn't invoke omp_finish_variant_function
+                where these errors would otherwise be caught.  */
+             if (is_initialized == SD_DEFAULTED)
+               {
+                 error_at (declarator->init_loc,
+                           "declare variant directives are not allowed on "
+                           "defaulted functions");
+                 return error_mark_node;
+               }
+             else if (is_initialized == SD_DELETED)
+               {
+                 error_at (declarator->init_loc,
+                           "declare variant directives are not allowed on "
+                           "deleted functions");
+                 return error_mark_node;
+               }
+           }
        }
     }
   else
@@ -27536,6 +27851,10 @@ cp_parser_class_specifier (cp_parser* parser)
   tree saved_ccr = current_class_ref;
   current_class_ptr = NULL_TREE;
   current_class_ref = NULL_TREE;
+  /* Set up for deferred lookup of "omp begin declare variant" base functions
+     in the class.  */
+  tree save_unregistered_variants = parser->omp_unregistered_variants;
+  parser->omp_unregistered_variants = NULL_TREE;
 
   /* Start the class.  */
   if (nested_name_specifier_p)
@@ -27557,6 +27876,19 @@ cp_parser_class_specifier (cp_parser* parser)
     /* Parse the member-specification.  */
     cp_parser_member_specification_opt (parser);
 
+  /* Register any "begin declare variant" functions in this class, since
+     references to the base function can only be resolved after the
+     entire class is seen.  */
+  for (tree bdv = parser->omp_unregistered_variants; bdv;
+       bdv = TREE_CHAIN (bdv))
+    {
+      tree dv_base = TREE_PURPOSE (TREE_PURPOSE (bdv));
+      tree dv_ctx = TREE_VALUE (TREE_PURPOSE (bdv));
+      tree dv_decl = TREE_VALUE (bdv);
+      omp_finish_variant_function (parser, dv_decl, dv_base, dv_ctx);
+    }
+  parser->omp_unregistered_variants = save_unregistered_variants;
+
   /* Look for the trailing `}'.  */
   closing_brace = braces.require_close (parser);
   /* Look for trailing attributes to apply to this class.  */
@@ -29216,6 +29548,28 @@ cp_parser_member_declaration (cp_parser* parser)
                  if (initializer && initializer_token_start)
                    error_at (initializer_token_start->location,
                              "pure-specifier on function-definition");
+
+                 /* If we're in an OpenMP "begin declare variant" block,
+                    the name in the declarator refers to the base function.
+                    We need to save that and modify the declarator to have
+                    the mangled name for the variant function instead.  */
+                 tree dv_base = NULL_TREE;
+                 tree dv_ctx = NULL_TREE;
+                 vec<cp_omp_declare_variant_attr, va_gc> *dv_state
+                   = scope_chain->omp_declare_variant_attribute;
+                 if (!vec_safe_is_empty (dv_state))
+                   {
+                     cp_omp_declare_variant_attr a = dv_state->last ();
+                     dv_ctx = copy_list (a.selector);
+                     dv_base = omp_start_variant_function (declarator,
+                                                           dv_ctx);
+                     if (dv_base == NULL_TREE)
+                       {
+                         cp_parser_skip_to_end_of_statement (parser);
+                         goto out;
+                       }
+                   }
+
                  decl = cp_parser_save_member_function_body (parser,
                                                              &decl_specifiers,
                                                              declarator,
@@ -29226,6 +29580,19 @@ cp_parser_member_declaration (cp_parser* parser)
                  /* If the member was not a friend, declare it here.  */
                  if (!friend_p)
                    finish_member_declaration (decl);
+
+                 /* If this function was in a "begin declare variant"
+                    block, record the information we need to find the
+                    base function and fix it up later.  At this point in
+                    parsing, we may not have seen the base function yet
+                    so we defer looking it up and registering the variant
+                    until the class is complete.  */
+                 if (dv_base && decl != error_mark_node)
+                   parser->omp_unregistered_variants
+                     = tree_cons (tree_cons (dv_base, dv_ctx, NULL_TREE),
+                                  decl,
+                                  parser->omp_unregistered_variants);
+
                  /* Peek at the next token.  */
                  token = cp_lexer_peek_token (parser->lexer);
                  /* If the next token is a semicolon, consume it.  */
@@ -50439,6 +50806,20 @@ cp_finish_omp_declare_variant (cp_parser *parser, 
cp_token *pragma_tok,
            goto fail;
          ctx = omp_check_context_selector (match_loc, ctx,
                                            OMP_CTX_DECLARE_VARIANT);
+
+         /* The OpenMP spec says the merging rules for enclosing
+            "begin declare variant" contexts apply to "declare variant
+            directives" -- the term it uses to refer to both directive
+            forms.  */
+         if (ctx != error_mark_node
+             && !vec_safe_is_empty 
(scope_chain->omp_declare_variant_attribute))
+           {
+             cp_omp_declare_variant_attr a
+               = scope_chain->omp_declare_variant_attribute->last ();
+             tree outer_ctx = a.selector;
+             ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx,
+                                                OMP_CTX_DECLARE_VARIANT);
+           }
          if (ctx != error_mark_node && variant != error_mark_node)
            {
              tree match_loc_node
@@ -51096,7 +51477,9 @@ cp_parser_omp_declare_target (cp_parser *parser, 
cp_token *pragma_tok)
 /* OpenMP 5.1
    # pragma omp begin assumes clauses[optseq] new-line
 
-   # pragma omp begin declare target clauses[optseq] new-line  */
+   # pragma omp begin declare target clauses[optseq] new-line
+
+   # pragma omp begin declare variant (match context-selector) new-line  */
 
 #define OMP_BEGIN_DECLARE_TARGET_CLAUSE_MASK                   \
        ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE_TYPE)  \
@@ -51142,9 +51525,73 @@ cp_parser_omp_begin (cp_parser *parser, cp_token 
*pragma_tok)
            = { in_omp_attribute_pragma, device_type, indirect };
          vec_safe_push (scope_chain->omp_declare_target_attribute, a);
        }
+      else if (strcmp (p, "variant") == 0)
+       {
+         cp_lexer_consume_token (parser->lexer);
+         const char *clause = "";
+         matching_parens parens;
+         location_t match_loc = cp_lexer_peek_token (parser->lexer)->location;
+         if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+           {
+             tree id = cp_lexer_peek_token (parser->lexer)->u.value;
+             clause = IDENTIFIER_POINTER (id);
+           }
+         if (strcmp (clause, "match") != 0)
+           {
+             cp_parser_error (parser, "expected %<match%>");
+             cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+             return;
+           }
+
+         cp_lexer_consume_token (parser->lexer);
+
+         if (!parens.require_open (parser))
+           {
+             cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+             return;
+           }
+
+         tree ctx = cp_parser_omp_context_selector_specification (parser,
+                                                                  true);
+         if (ctx != error_mark_node)
+           ctx = omp_check_context_selector (match_loc, ctx,
+                                             OMP_CTX_BEGIN_DECLARE_VARIANT);
+
+         if (ctx != error_mark_node
+             && !vec_safe_is_empty 
(scope_chain->omp_declare_variant_attribute))
+           {
+             cp_omp_declare_variant_attr a
+               = scope_chain->omp_declare_variant_attribute->last ();
+             tree outer_ctx = a.selector;
+             ctx = omp_merge_context_selectors (match_loc, outer_ctx, ctx,
+                                                OMP_CTX_BEGIN_DECLARE_VARIANT);
+           }
+
+         if (ctx == error_mark_node
+             || !omp_context_selector_matches (ctx, NULL_TREE, false, true))
+           {
+             /* The context is either invalid or cannot possibly match.
+                In the latter case the spec says all code in the begin/end
+                sequence will be elided.  In the former case we'll get bogus
+                errors from trying to parse it without a valid context to
+                use for name-mangling, so elide that too.  */
+             cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+             cp_parser_skip_to_pragma_omp_end_declare_variant (parser);
+             return;
+           }
+         else
+           {
+             cp_omp_declare_variant_attr a
+               = { parser->lexer->in_omp_attribute_pragma, ctx };
+             vec_safe_push (scope_chain->omp_declare_variant_attribute, a);
+           }
+
+         parens.require_close (parser);
+         cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+       }
       else
        {
-         cp_parser_error (parser, "expected %<target%>");
+         cp_parser_error (parser, "expected %<target%> or %<variant%>");
          cp_parser_skip_to_pragma_eol (parser, pragma_tok);
        }
     }
@@ -51157,7 +51604,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token 
*pragma_tok)
     }
   else
     {
-      cp_parser_error (parser, "expected %<declare target%> or %<assumes%>");
+      cp_parser_error (parser, "expected %<declare target%>, "
+                      "%<declare variant%>, or %<assumes%>");
       cp_parser_skip_to_pragma_eol (parser, pragma_tok);
     }
 }
@@ -51166,7 +51614,8 @@ cp_parser_omp_begin (cp_parser *parser, cp_token 
*pragma_tok)
    # pragma omp end declare target new-line
 
    OpenMP 5.1:
-   # pragma omp end assumes new-line  */
+   # pragma omp end assumes new-line
+   # pragma omp end declare variant new-line  */
 
 static void
 cp_parser_omp_end (cp_parser *parser, cp_token *pragma_tok)
@@ -51188,41 +51637,70 @@ cp_parser_omp_end (cp_parser *parser, cp_token 
*pragma_tok)
          p = IDENTIFIER_POINTER (id);
        }
       if (strcmp (p, "target") == 0)
-       cp_lexer_consume_token (parser->lexer);
+       {
+         cp_lexer_consume_token (parser->lexer);
+         cp_parser_require_pragma_eol (parser, pragma_tok);
+         if (!vec_safe_length (scope_chain->omp_declare_target_attribute))
+           error_at (pragma_tok->location,
+                     "%<#pragma omp end declare target%> without "
+                     "corresponding %<#pragma omp declare target%> or "
+                     "%<#pragma omp begin declare target%>");
+         else
+           {
+             cp_omp_declare_target_attr
+               a = scope_chain->omp_declare_target_attribute->pop ();
+             if (a.attr_syntax != in_omp_attribute_pragma)
+               {
+                 if (a.attr_syntax)
+                   error_at (pragma_tok->location,
+                             "%qs in attribute syntax terminated "
+                             "with %qs in pragma syntax",
+                             a.device_type >= 0 ? "begin declare target"
+                                                : "declare target",
+                             "end declare target");
+                 else
+                   error_at (pragma_tok->location,
+                             "%qs in pragma syntax terminated "
+                             "with %qs in attribute syntax",
+                             a.device_type >= 0 ? "begin declare target"
+                                                : "declare target",
+                             "end declare target");
+               }
+           }
+       }
+      else if (strcmp (p, "variant") == 0)
+       {
+         cp_lexer_consume_token (parser->lexer);
+         cp_parser_require_pragma_eol (parser, pragma_tok);
+         if (!vec_safe_length (scope_chain->omp_declare_variant_attribute))
+           error_at (pragma_tok->location,
+                     "%<#pragma omp end declare variant%> without "
+                     "corresponding %<#pragma omp begin declare variant%>");
+         else
+           {
+             cp_omp_declare_variant_attr
+               a = scope_chain->omp_declare_variant_attribute->pop ();
+             if (a.attr_syntax != in_omp_attribute_pragma)
+               {
+                 if (a.attr_syntax)
+                   error_at (pragma_tok->location,
+                             "%<begin declare variant%> in attribute syntax "
+                             "terminated with %<end declare variant%> in "
+                             "pragma syntax");
+                 else
+                   error_at (pragma_tok->location,
+                             "%<begin declare variant%> in pragma syntax "
+                             "terminated with %<end declare variant%> in "
+                             "attribute syntax");
+               }
+           }
+       }
       else
        {
          cp_parser_error (parser, "expected %<target%>");
          cp_parser_skip_to_pragma_eol (parser, pragma_tok);
          return;
        }
-      cp_parser_require_pragma_eol (parser, pragma_tok);
-      if (!vec_safe_length (scope_chain->omp_declare_target_attribute))
-       error_at (pragma_tok->location,
-                 "%<#pragma omp end declare target%> without corresponding "
-                 "%<#pragma omp declare target%> or "
-                 "%<#pragma omp begin declare target%>");
-      else
-       {
-         cp_omp_declare_target_attr
-           a = scope_chain->omp_declare_target_attribute->pop ();
-         if (a.attr_syntax != in_omp_attribute_pragma)
-           {
-             if (a.attr_syntax)
-               error_at (pragma_tok->location,
-                         "%qs in attribute syntax terminated "
-                         "with %qs in pragma syntax",
-                         a.device_type >= 0 ? "begin declare target"
-                                            : "declare target",
-                         "end declare target");
-             else
-               error_at (pragma_tok->location,
-                         "%qs in pragma syntax terminated "
-                         "with %qs in attribute syntax",
-                         a.device_type >= 0 ? "begin declare target"
-                                            : "declare target",
-                         "end declare target");
-           }
-       }
     }
   else if (strcmp (p, "assumes") == 0)
     {
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index f9ed80123c4..d998f7e9af7 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -456,6 +456,11 @@ struct GTY(()) cp_parser {
      outside that file.  */
   struct omp_metadirective_parse_data * GTY((skip))
     omp_metadirective_state;
+
+  /* TREE_LIST of "omp begin declare variant" functions when the base
+     function has not been seen "elsewhere" (per the OpenMP spec) yet;
+     used only when these functions are seen in a class definition.  */
+  tree omp_unregistered_variants;
 };
 
 /* In parser.cc  */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index a2ee3a34caa..7a38dfb76e3 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3963,6 +3963,13 @@ finish_translation_unit (void)
               "#pragma omp end declare target");
       vec_safe_truncate (scope_chain->omp_declare_target_attribute, 0);
     }
+  if (vec_safe_length (scope_chain->omp_declare_variant_attribute))
+    {
+      if (!errorcount)
+       error ("%<omp begin declare variant%> without corresponding "
+              "%<omp end declare variant%>");
+      vec_safe_truncate (scope_chain->omp_declare_variant_attribute, 0);
+    }
   if (vec_safe_length (scope_chain->omp_begin_assumes))
     {
       if (!errorcount)
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C
new file mode 100644
index 00000000000..dfeb7c42d08
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable -fdump-tree-gimple" } */
+
+/* Check that variants within a "begin declare variant" directive 
+   are attached to the correct overloaded function.  */
+
+int f (int x) { return x; }
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+int f (int x) { return -1; }
+#pragma omp end declare variant
+
+int f (int x, int y) { return x * y; }
+
+#pragma omp begin declare variant match (construct={target})
+int f (int x, int y) { return -2; }
+#pragma omp end declare variant
+
+int f (int x, int y, int z) { return x * y * z; }
+
+#pragma omp begin declare variant match (device={kind("host")})
+int f (int x, int y, int z) { return -3; }
+#pragma omp end declare variant
+
+int main (void)
+{
+  if (f (10) != -1) __builtin_abort ();
+  if (f (10, 20) != 200) __builtin_abort ();   /* no match on this one */
+  if (f (10, 20, 30) != -3) __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump "f\\.ompvariant. \\(10\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "f \\(10, 20\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "f\\.ompvariant. \\(10, 20, 30\\)" "gimple" } } 
*/
+
+
+
+
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C
new file mode 100644
index 00000000000..1784e14dfc3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Check that "omp begin declare variant" works on methods in a 
+   class declaration.  */
+
+class test1 {
+
+ private:
+  int n;
+  static int m;
+
+ public:
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  int get_n (void) { return n * 2; }
+  static int get_m (void) { return m * 2; }
+  #pragma omp end declare variant
+
+  #pragma omp begin declare variant match (construct={target})
+  int get_n (void) { return this->n * 2; }
+  #pragma omp end declare variant
+
+  /* The base methods are deliberately declared after the variants in order
+     to check that the lookup can still find them.  */
+  void set_n (int x) { n = x; }
+  int get_n (void) { return n; }
+  
+  static void set_m (int x) { m = x; }
+  static int get_m (void) { return m; }
+};
+
+int test1::m;
+
+int main (void)
+{
+  test1 t1;
+  t1.set_n (10);
+  if (t1.get_n () != 20) __builtin_abort ();
+  test1::set_m (1);
+  if (test1::get_m () != 2) __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump "test1::get_n\\.ompvariant. \\(&t1\\)" "gimple" 
} } */
+/* { dg-final { scan-tree-dump "test1::get_m\\.ompvariant. \\(\\)" "gimple" } 
} */
+
+/* The variants must have internal linkage, not .globl or .weak.  */
+/* { dg-final { scan-assembler-not "\\.globl\[ 
\t\]*_?_ZN5test117get_n\\.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.globl\[ 
\t\]*_?_ZN5test117get_m\\.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.weak\[ 
\t\]*_?_ZN5test117get_n\\.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.weak\[ 
\t\]*_?_ZN5test117get_m\\.ompvariant" } } */
+
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C
new file mode 100644
index 00000000000..ccbb01ce794
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+
+/* Check that "omp begin declare variant" for class methods outside of the
+   class declaration gives a sorry.  C++ generally does not allow injection
+   of additional methods into a class outside of its declaration so it is
+   not clear what this is supposed to do.  */
+
+class test1 {
+
+ private:
+  int n;
+  static int m;
+
+ public:
+
+  void set_n (int x) { n = x; }
+  int get_n (void) { return n; }
+  
+  static void set_m (int x) { m = x; }
+  static int get_m (void) { return m; }
+
+};
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+int test1::get_n (void) { return n * 2; }  /* { dg-message "sorry, 
unimplemented: cannot handle qualified name for variant function" } */
+static int test1::get_m (void) { return m * 2; }  /* { dg-message "sorry, 
unimplemented: cannot handle qualified name for variant function" } */
+#pragma omp end declare variant
+
+int main (void)
+{
+  test1 t1;
+  t1.set_n (10);
+  if (t1.get_n () != 20) __builtin_abort ();
+  test1::set_m (1);
+  if (test1::get_m () != 2) __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C
new file mode 100644
index 00000000000..567cf9c4f8c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Like c-c++-common/delim-declare-variant-1.c, but with namespaces.  */
+
+namespace n1 {
+
+int foo (int a)
+{
+  return a;
+}
+
+int bar (int x)
+{
+  return x;
+}
+
+#pragma omp begin declare variant match (construct={target})
+int foo (int a)
+{
+  return a + 1;
+}
+
+int bar (int x)
+{
+  return x * 2;
+}
+#pragma omp end declare variant
+
+/* Because of the high score value, this variant for "bar" should always be
+   selected even when the one above also matches.  */
+#pragma omp begin declare variant match 
(implementation={vendor(score(10000):"gnu")})
+int bar (int x)
+{
+  return x * 4;
+}
+#pragma omp end declare variant
+
+} /* namespace n1 */
+
+int main (void)
+{
+  if (n1::foo (42) != 42) __builtin_abort ();
+  if (n1::bar (3) != 12) __builtin_abort ();
+#pragma omp target
+  {
+    if (n1::foo (42) != 43) __builtin_abort ();
+    if (n1::bar (3) != 12) __builtin_abort ();
+  }
+}
+
+/* { dg-final { scan-tree-dump-times "omp declare variant base 
\\(foo.ompvariant." 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "omp declare variant base 
\\(bar.ompvariant." 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "foo \\(42\\)" 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "foo\\.ompvariant. \\(42\\)" 1 "gimple" } 
} */
+/* { dg-final { scan-tree-dump-times "bar \\(3\\)" 0 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "bar\\.ompvariant. \\(3\\)" 2 "gimple" } 
} */
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C
new file mode 100644
index 00000000000..4f35d209d25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C
@@ -0,0 +1,51 @@
+// { dg-do compile }
+
+// Check that variants for a template function are instantiated correctly.
+// FIXME:  Fails due to PR118530.  
+
+template<typename T>
+void f_default_param (T = 42) {}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void f_default_param (T = 42) {}
+#pragma omp end declare variant
+
+template<typename T>
+void f_no_param () {}  // { dg-bogus "no matching function for call" 
"PR118530" { xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void f_no_param () {}
+#pragma omp end declare variant
+
+void instantiate_f()
+{
+  f_default_param<int>();
+  f_no_param<int>();
+}
+
+template<int>
+void nttp () {}  // { dg-bogus "no matching function for call" "PR118530" { 
xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<int>
+void nttp () {}
+#pragma omp end declare variant
+
+void instantiate_nttp()
+{
+  nttp<42>();
+}
+
+template<typename>
+struct S {};
+
+template<template<typename> class Templ>
+void templ_templ () {}  // { dg-bogus "no matching function for call" 
"PR118530" { xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<template<typename> class Templ>
+void templ_templ () {}
+#pragma omp end declare variant
+
+void instantiate_templ_templ()
+{
+  templ_templ<S>();
+}
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C
new file mode 100644
index 00000000000..38e41e7847b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C
@@ -0,0 +1,31 @@
+/* { dg-do compile { target c++11 } } */
+
+/* This test case fails in omp_finish_variant_function because base_decl
+   is a SCOPE_REF and it cannot be resolved to an actual base function decl
+   to hang the variant attribute on.  */
+
+template<typename T, typename U>
+  class is_same {
+  static constexpr bool value = false;
+};
+
+template<typename T>
+class is_same<T, T> {
+  static constexpr bool value = true;
+};
+
+template<typename T>
+void fn (T&&) { }
+
+#pragma omp begin declare variant match(implementation={vendor("gnu")})
+template<typename T>
+void fn(T&&) {  // { dg-bogus "base function cannot be resolved" "" { xfail 
*-*-* } }
+  static_assert(is_same<T, int>::value);
+}
+#pragma omp end declare variant
+
+int main()
+{
+  int lvalue = 42;
+  fn(0);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C
new file mode 100644
index 00000000000..e8db369a67d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+
+/* Check that "omp begin declare variant" for a namespace function outside of
+   the namespace gives an error.  C++ generally does not allow injection of
+   additional function into a namespace outside of its scope so this is just a
+   generic error.  */
+
+namespace n1 {
+
+int foo (int a)
+{
+  return a;
+}
+
+int bar (int x)
+{
+  return x;
+}
+
+} /* namespace n1 */
+
+
+#pragma omp begin declare variant match (construct={target})
+int n1::foo (int a)  /* { dg-message "sorry, unimplemented: cannot handle 
qualified name for variant function" } */
+{
+  return a + 1;
+}
+
+int n1::bar (int x)  /* { dg-message "sorry, unimplemented: cannot handle 
qualified name for variant function" } */
+{
+  return x * 2;
+}
+#pragma omp end declare variant
+
+/* Because of the high score value, this variant for "bar" should always be
+   selected even when the one above also matches.  */
+#pragma omp begin declare variant match 
(implementation={vendor(score(10000):"gnu")})
+int n1::bar (int x)  /* { dg-message "sorry, unimplemented: cannot handle 
qualified name for variant function" } */
+{
+  return x * 4;
+}
+#pragma omp end declare variant
+
+int main (void)
+{
+  if (n1::foo (42) != 42) __builtin_abort ();
+  if (n1::bar (3) != 12) __builtin_abort ();
+#pragma omp target
+  {
+    if (n1::foo (42) != 43) __builtin_abort ();
+    if (n1::bar (3) != 12) __builtin_abort ();
+  }
+}
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C
new file mode 100644
index 00000000000..a958b52d01e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C
@@ -0,0 +1,99 @@
+/* { dg-do compile } */
+
+/* Test for restrictions on declare variant functions on virtual functions,
+   constructors, and destructors.  */
+
+struct S0
+{
+  virtual void f_virtual_before0 () {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  virtual void f_virtual_before0 () {}  // { dg-error "declare variant 
directives are not allowed on virtual functions" }
+  #pragma omp end declare variant
+
+  virtual void f_virtual_before1 () {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  void f_virtual_before1 () {}  // { dg-error "declare variant directives are 
not allowed on virtual functions" }
+  #pragma omp end declare variant
+
+  void f_virtual_before2 () {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  virtual void f_virtual_before2 () {}  // { dg-error "declare variant 
directives are not allowed on virtual functions" }
+  #pragma omp end declare variant
+
+  void f_virtual_before3 () {}
+  // code elision, no error
+  #pragma omp begin declare variant match (implementation={vendor("cray")})
+  virtual void f_virtual_before3 () {}
+  #pragma omp end declare variant
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  virtual void f_virtual_after0 () {}  // { dg-error "declare variant 
directives are not allowed on virtual functions" }
+  #pragma omp end declare variant
+  virtual void f_virtual_after0 () {}
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  void f_virtual_after1 () {}  // { dg-error "declare variant directives are 
not allowed on virtual functions" }
+  #pragma omp end declare variant
+  virtual void f_virtual_after1 () {}
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  virtual void f_virtual_after2 () {}  // { dg-error "declare variant 
directives are not allowed on virtual functions" }
+  #pragma omp end declare variant
+  void f_virtual_after2 () {}
+};
+
+struct S_before {
+  S_before() {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_before() {}  // { dg-error "declare variant directives are not allowed on 
constructors" }
+  #pragma omp end declare variant
+
+  S_before(int) {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_before(int) {}  // { dg-error "declare variant directives are not allowed 
on constructors" }
+  #pragma omp end declare variant
+
+  S_before(double) {}
+  // code elision, no error
+  #pragma omp begin declare variant match (implementation={vendor("cray")})
+  S_before(double) {}
+  #pragma omp end declare variant
+  
+  template<typename T>
+  S_before(T) {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  template<typename T>
+  S_before(T) {}  // { dg-error "declare variant directives are not allowed on 
constructors" }
+  #pragma omp end declare variant
+
+  ~S_before() {}
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  ~S_before() {}  // { dg-error "declare variant directives are not allowed on 
destructors" }
+  #pragma omp end declare variant
+};
+
+struct S_after {
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_after() {}  // { dg-error "declare variant directives are not allowed on 
constructors" }
+  #pragma omp end declare variant
+  S_after() {}
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_after(int) {}  // { dg-error "declare variant directives are not allowed 
on constructors" }
+  #pragma omp end declare variant
+  S_after(int) {}
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  template<typename T>
+  S_after(T) {}  // { dg-error "declare variant directives are not allowed on 
constructors" }
+  #pragma omp end declare variant
+  template<typename T>
+  S_after(T) {}
+
+  // code elision, no error
+  #pragma omp begin declare variant match (implementation={vendor("cray")})
+  ~S_after() {}
+  #pragma omp end declare variant
+  ~S_after() {}
+};
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C
new file mode 100644
index 00000000000..9e65309b0df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C
@@ -0,0 +1,181 @@
+/* { dg-do compile { target c++11 } } */
+
+/* Test delimited declare variant on constexpr, deleted, and defaulted
+   functions.  */
+/* C++11 */
+
+/* TODO: add templates cases for constexpr/delete free functions */
+
+/* Do we warn for the mismatch?
+   TBH we probably warn whenever a variant function is constexpr in general.
+   I can't imagine that we are going to support constant evaluation of a
+   variant function, realistically the only choice is to always use the base
+   function if a constant-expression is required.  */
+constexpr int freefn_mismatched_constexpr_before0 () { return 0; }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+int freefn_mismatched_constexpr_before0 () { return 1; }
+#pragma omp end declare variant
+
+int freefn_mismatched_constexpr_before1 () { return 0; }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+constexpr int freefn_mismatched_constexpr_before1 () { return 1; }
+#pragma omp end declare variant
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+constexpr int freefn_mismatched_constexpr_after0 () { return 1; }  // { 
dg-error "no previous declaration of base function" }
+#pragma omp end declare variant
+int freefn_mismatched_constexpr_after0 () { return 0; }
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+int freefn_mismatched_constexpr_after1 () { return 1; }  // { dg-error "no 
previous declaration of base function" }
+#pragma omp end declare variant
+constexpr int freefn_mismatched_constexpr_after1 () { return 0; }
+
+
+
+void freefn_deleted_before () = delete;
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_deleted_before () {}  // { dg-error "declare variant directives 
are not allowed on deleted functions" }
+#pragma omp end declare variant
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_deleted_after () {}  // { dg-error "no previous declaration of 
base function" }
+#pragma omp end declare variant
+void freefn_deleted_after () = delete;
+
+/* TECHNICALLY allowed by the spec, but obviously conflicts with the 
intention.  */
+void freefn_variant_deleted_base_before () {}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_variant_deleted_base_before () = delete;  // { dg-error "declare 
variant directives are not allowed on deleted functions" }
+#pragma omp end declare variant
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_variant_deleted_base_after () = delete;  // { dg-error "declare 
variant directives are not allowed on deleted functions" }
+#pragma omp end declare variant
+void freefn_variant_deleted_base_after () {};
+
+
+/* For now, obviously error, not sure if we error on just the base or on
+   both though.
+   In the future, I think if the base and all variants are deleted, we can
+   treat a call to the function as deleted before we determine a variant.  */
+void freefn_both_deleted_base_before () = delete;
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_both_deleted_base_before () = delete;  // { dg-error "declare 
variant directives are not allowed on deleted functions" }
+#pragma omp end declare variant
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_both_deleted_base_after () = delete;  // { dg-error "declare 
variant directives are not allowed on deleted functions" }
+#pragma omp end declare variant
+void freefn_both_deleted_base_after () = delete;
+
+
+
+
+struct S0
+{
+  void f_deleted_before () = delete;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  void f_deleted_before () {}  // { dg-error "declare variant directives are 
not allowed on deleted functions" }
+  #pragma omp end declare variant
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  void f_deleted_after () {}  // { dg-error "declare variant directives are 
not allowed on deleted functions" }
+  #pragma omp end declare variant
+  void f_deleted_after () = delete;
+};
+
+
+/* These should error for constructor/destructor, not default.  */
+struct S_default_before {
+  S_default_before() = default;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_before() {}  // { dg-error "declare variant directives are not 
allowed on constructors" }
+  #pragma omp end declare variant
+
+  S_default_before(S_default_before const&) = default;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_before(S_default_before const&) {}  // { dg-error "declare variant 
directives are not allowed on constructors" }
+  #pragma omp end declare variant
+
+  S_default_before(S_default_before&&) = default;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_before(S_default_before&&) {}  // { dg-error "declare variant 
directives are not allowed on constructors" }
+  #pragma omp end declare variant
+
+  ~S_default_before() = default;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  ~S_default_before() {}  // { dg-error "declare variant directives are not 
allowed on destructors" }
+  #pragma omp end declare variant
+};
+
+struct S_default_after {
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_after() {}  // { dg-error "declare variant directives are not 
allowed on constructors" }
+  #pragma omp end declare variant
+  S_default_after() = default;
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_after(S_default_after const&) {}  // { dg-error "declare variant 
directives are not allowed on constructors" }
+  #pragma omp end declare variant
+  S_default_after(S_default_after const&) = default;
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_after(S_default_after&&) {}  // { dg-error "declare variant 
directives are not allowed on constructors" }
+  #pragma omp end declare variant
+  S_default_after(S_default_after&&) = default;
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  ~S_default_after() {}  // { dg-error "declare variant directives are not 
allowed on destructors" }
+  #pragma omp end declare variant
+  ~S_default_after() = default;
+};
+
+/* These should error for default/delete.  */
+struct S_default_assignment_before {
+  S_default_assignment_before& operator=(S_default_assignment_before const&) = 
default;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_assignment_before& operator=(S_default_assignment_before const&) { 
return *this; }  // { dg-error "declare variant directives are not allowed on 
defaulted functions" }
+  #pragma omp end declare variant
+
+  S_default_assignment_before& operator=(S_default_assignment_before&&) = 
default;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_assignment_before& operator=(S_default_assignment_before&&) { 
return *this; }  // { dg-error "declare variant directives are not allowed on 
defaulted functions" }
+  #pragma omp end declare variant
+};
+
+struct S_default_assignment_after {
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_assignment_after& operator=(S_default_assignment_after const&) { 
return *this; }  // { dg-error "declare variant directives are not allowed on 
defaulted functions" }
+  #pragma omp end declare variant
+  S_default_assignment_after& operator=(S_default_assignment_after const&) = 
default;
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_default_assignment_after& operator=(S_default_assignment_after&&) { return 
*this; }  // { dg-error "declare variant directives are not allowed on 
defaulted functions" }
+  #pragma omp end declare variant
+  S_default_assignment_after& operator=(S_default_assignment_after&&) = 
default;
+};
+
+struct S_deleted_assignment_before {
+  S_deleted_assignment_before& operator=(S_deleted_assignment_before const&) = 
delete;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_deleted_assignment_before& operator=(S_deleted_assignment_before const&) { 
return *this; }  // { dg-error "declare variant directives are not allowed on 
deleted functions" }
+  #pragma omp end declare variant
+
+  S_deleted_assignment_before& operator=(S_deleted_assignment_before&&) = 
delete;
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_deleted_assignment_before& operator=(S_deleted_assignment_before&&) { 
return *this; }  // { dg-error "declare variant directives are not allowed on 
deleted functions" }
+  #pragma omp end declare variant
+};
+
+struct S_deleted_assignment_after {
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_deleted_assignment_after& operator=(S_deleted_assignment_after const&) { 
return *this; }  // { dg-error "declare variant directives are not allowed on 
deleted functions" }
+  #pragma omp end declare variant
+  S_deleted_assignment_after& operator=(S_deleted_assignment_after const&) = 
delete;
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  S_deleted_assignment_after& operator=(S_deleted_assignment_after&&) { return 
*this; }  // { dg-error "declare variant directives are not allowed on deleted 
functions" }
+  #pragma omp end declare variant
+  S_deleted_assignment_after& operator=(S_deleted_assignment_after&&) = delete;
+};
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C
new file mode 100644
index 00000000000..4f4a005c6c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C
@@ -0,0 +1,24 @@
+/* { dg-do compile { target c++20 } } */
+
+/* The procedure that a declare variant directive determined to be a function
+   variant may not be an immediate function + Declare variant directives may
+   not be specified for immediate functions. */
+consteval void freefn_consteval_before0 () {}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+consteval void freefn_consteval_before0 () {}  // { dg-error "declare variant 
directives are not allowed on immediate functions" }
+#pragma omp end declare variant
+
+/* Declare variant directives may not be specified for immediate functions.  */
+consteval void freefn_consteval_before1 () {}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+void freefn_consteval_before1 () {}  // { dg-error "declare variant directives 
are not allowed on immediate functions" }
+#pragma omp end declare variant
+
+/* The procedure that a declare variant directive determined to be a function
+   variant may not be an immediate function.  */
+void freefn_consteval_before2 () {}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+consteval void freefn_consteval_before2 () {}  // { dg-error "declare variant 
directives are not allowed on immediate functions" }
+#pragma omp end declare variant
+
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C
new file mode 100644
index 00000000000..30dee4cc607
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Check "begin declare variant" on template functions.  */
+
+template <typename T>
+T foo (T a)
+{
+  return a;
+}
+
+template <typename T>
+T bar (T x)
+{
+  return x;
+}
+
+#pragma omp begin declare variant match (construct={target})
+template <typename T1>
+T1 foo (T1 a)
+{
+  return a + 1;
+}
+
+template <typename T1>
+T1 bar (T1 x)
+{
+  return x * 2;
+}
+#pragma omp end declare variant
+
+/* Because of the high score value, this variant for "bar" should always be
+   selected even when the one above also matches.  */
+#pragma omp begin declare variant match 
(implementation={vendor(score(10000):"gnu")})
+template <typename T2>
+T2 bar (T2 x)
+{
+  return x * 4;
+}
+#pragma omp end declare variant
+
+int main (void)
+{
+  if (foo<int> (42) != 42) __builtin_abort ();
+  if (bar<int> (3) != 12) __builtin_abort ();
+#pragma omp target
+  {
+    if (foo<int> (42) != 43) __builtin_abort ();
+    if (bar<int> (3) != 12) __builtin_abort ();
+  }
+}
+
+/* Make sure all the template functions are instantiated.  */
+/* { dg-final { scan-tree-dump "int foo.ompvariant.<int> \\(.*\\)" "gimple" } }
+/* { dg-final { scan-tree-dump "int foo<int> \\(.*\\)" "gimple" } }
+/* { dg-final { scan-tree-dump "int bar.ompvariant.<int> \\(.*\\)" "gimple" } }
+
+/* Make sure the calls are resolved correctly.  */
+/* { dg-final { scan-tree-dump-times "foo<int> \\(42\\)" 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "foo\\.ompvariant.<int> \\(42\\)" 1 
"gimple" } } */
+/* { dg-final { scan-tree-dump-times "bar<int> \\(3\\)" 0 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "bar\\.ompvariant.<int> \\(3\\)" 2 
"gimple" } } */
+
+/* The variants must have internal linkage, not .globl or .weak.  */
+/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_Z15foo.ompvariant" } } 
*/
+/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_Z15bar.ompvariant" } } 
*/
+/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_Z15foo.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_Z15bar.ompvariant" } } */
+
+
+
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C
new file mode 100644
index 00000000000..b24e6c0cb3b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+/* Check that "omp begin declare variant" works on methods in a template
+   class declaration.  */
+
+template <typename T>
+class test1 {
+
+ private:
+  T n;
+  static T m;
+
+ public:
+
+  void set_n (T x) { n = x; }
+  T get_n (void) { return n; }
+  
+  static void set_m (T x) { m = x; }
+  static T get_m (void) { return m; }
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  T get_n (void) { return n * 2; }
+  static T get_m (void) { return m * 2; }
+  #pragma omp end declare variant
+
+  #pragma omp begin declare variant match (construct={target})
+  T get_n (void) { return this->n * 2; }
+  #pragma omp end declare variant
+};
+
+template <typename T>
+T test1<T>::m;
+
+int main (void)
+{
+  test1<int> t1;
+  t1.set_n (10);
+  if (t1.get_n () != 20) __builtin_abort ();
+  test1<int>::set_m (1);
+  if (test1<int>::get_m () != 2) __builtin_abort ();
+}
+
+/* Make sure the "declare variant" replacement happens.  */
+/* { dg-final { scan-tree-dump "test1<int>::get_n\\.ompvariant. \\(&t1\\)" 
"gimple" } } */
+/* { dg-final { scan-tree-dump "test1<int>::get_m\\.ompvariant. \\(\\)" 
"gimple" } } */
+
+/* Make sure the variant methods are instantiated.  */
+/* { dg-final { scan-tree-dump "int test1<int>::get_n\\.ompvariant. \\(.*\\)" 
"gimple" } }  */
+/* { dg-final { scan-tree-dump "int test1<int>::get_m\\.ompvariant. \\(.*\\)" 
"gimple" } }  */
+
+/* The variants must have internal linkage, not .globl or .weak.  */
+/* { dg-final { scan-assembler-not "\\.globl\[ 
\t\]*_?_ZN5test1IiE17get_n.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.globl\[ 
\t\]*_?_ZN5test1IiE17get_m.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.weak\[ 
\t\]*_?_ZN5test1IiE17get_n.ompvariant" } } */
+/* { dg-final { scan-assembler-not "\\.weak\[ 
\t\]*_?_ZN5test1IiE17get_m.ompvariant" } } */
+
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C
new file mode 100644
index 00000000000..ed1e1ae2ddd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C
@@ -0,0 +1,206 @@
+/* { dg-do compile { target c++11 } } */
+
+/* Check that the substituted type in variant is the same as the one in the
+   base.  */
+
+template<typename T, typename U>
+struct is_same {
+  static constexpr bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T> {
+  static constexpr bool value = true;
+};
+
+/* Using static_assert directly in a variant triggers the SCOPE_REF bug noted
+   in delim-declare-variant-41.C.  We'll avoid that by outsourcing the checks
+   to this function.  PR118791 is a different bug that affects also the
+   non-delimited form of "declare variant".  */
+template<typename T, typename U>
+void fail_if_not_same() {
+  static_assert(is_same<T, U>::value);  // { dg-bogus "static assertion 
failed" "PR118791" { xfail *-*-* } }
+}
+
+/* Sanity checks are included in the base function just to be absolutely
+   certain there were no mistakes made in the tests.  They should match the
+   cases in the variant function exactly.  */
+
+template<typename T>
+void fwdref_passed_lvalue_int (T&& p) {
+  static_assert(is_same<T, int&>::value);
+  static_assert(is_same<decltype(p), int&>::value);
+  static_assert(is_same<decltype((p)), int&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void fwdref_passed_lvalue_int (T&& p) {
+  fail_if_not_same<T, int&>();
+  fail_if_not_same<decltype(p), int&>();
+  fail_if_not_same<decltype((p)), int&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void fwdref_passed_lvalue_const_int (T&& p) {
+  static_assert(is_same<T, int const&>::value);
+  static_assert(is_same<decltype(p), int const&>::value);
+  static_assert(is_same<decltype((p)), int const&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void fwdref_passed_lvalue_const_int (T&& p) {
+  fail_if_not_same<T, int const&>();
+  fail_if_not_same<decltype(p), int const&>();
+  fail_if_not_same<decltype((p)), int const&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void fwdref_passed_rvalue_int (T&& p) {
+  static_assert(is_same<T, int>::value);
+  static_assert(is_same<decltype(p), int&&>::value);
+  static_assert(is_same<decltype((p)), int&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void fwdref_passed_rvalue_int (T&& p) {
+  fail_if_not_same<T, int>();
+  fail_if_not_same<decltype(p), int&&>();
+  fail_if_not_same<decltype((p)), int&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void fwdref_passed_rvalue_const_int (T&& p) {
+  static_assert(is_same<T, int const>::value);
+  static_assert(is_same<decltype(p), int const&&>::value);
+  static_assert(is_same<decltype((p)), int const&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void fwdref_passed_rvalue_const_int (T&& p) {
+  fail_if_not_same<T, int const>();
+  fail_if_not_same<decltype(p), int const&&>();
+  fail_if_not_same<decltype((p)), int const&>();
+}
+#pragma omp end declare variant
+
+void instantiate_fwdref()
+{
+  int lvalue = 0;
+  fwdref_passed_lvalue_int(lvalue);
+  fwdref_passed_lvalue_const_int(static_cast<int const&>(lvalue));
+  fwdref_passed_rvalue_int(0);
+  fwdref_passed_rvalue_const_int(static_cast<int const&&>(0));
+}
+
+
+
+template<typename T>
+void explicit_instantiate_fwdref_with_lvalue_int (T&& p) {
+  static_assert(is_same<T, int&>::value);
+  static_assert(is_same<decltype(p), int&>::value);
+  static_assert(is_same<decltype((p)), int&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void explicit_instantiate_fwdref_with_lvalue_int (T&& p) {
+  fail_if_not_same<T, int&>();
+  fail_if_not_same<decltype(p), int&>();
+  fail_if_not_same<decltype((p)), int&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void explicit_instantiate_fwdref_with_lvalue_const_int (T&& p) {
+  static_assert(is_same<T, int const&>::value);
+  static_assert(is_same<decltype(p), int const&>::value);
+  static_assert(is_same<decltype((p)), int const&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void explicit_instantiate_fwdref_with_lvalue_const_int (T&& p) {
+  fail_if_not_same<T, int const&>();
+  fail_if_not_same<decltype(p), int const&>();
+  fail_if_not_same<decltype((p)), int const&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void explicit_instantiate_fwdref_with_rvalue_int (T&& p) {
+  static_assert(is_same<T, int&&>::value);
+  static_assert(is_same<decltype(p), int&&>::value);
+  static_assert(is_same<decltype((p)), int&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void explicit_instantiate_fwdref_with_rvalue_int (T&& p) {
+  fail_if_not_same<T, int&&>();
+  fail_if_not_same<decltype(p), int&&>();
+  fail_if_not_same<decltype((p)), int&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void explicit_instantiate_fwdref_with_rvalue_const_int (T&& p) {
+  static_assert(is_same<T, int const&&>::value);
+  static_assert(is_same<decltype(p), int const&&>::value);
+  static_assert(is_same<decltype((p)), int const&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void explicit_instantiate_fwdref_with_rvalue_const_int (T&& p) {
+  fail_if_not_same<T, int const&&>();
+  fail_if_not_same<decltype(p), int const&&>();
+  fail_if_not_same<decltype((p)), int const&>();
+}
+#pragma omp end declare variant
+
+/* Technically a missuse of a forwarding reference */
+void explicit_instantiate_fwdref()
+{
+  int lvalue = 0;
+  explicit_instantiate_fwdref_with_lvalue_int<int&>(lvalue);
+  explicit_instantiate_fwdref_with_lvalue_const_int<int 
const&>(static_cast<int const&>(lvalue));
+  explicit_instantiate_fwdref_with_rvalue_int<int&&>(0);  // { dg-bogus 
"required from here" "PR118791" { xfail *-*-* } }
+  explicit_instantiate_fwdref_with_rvalue_const_int<int 
const&&>(static_cast<int const&&>(0));  // { dg-bogus "required from here" 
"PR118791" { xfail *-*-* } }
+}
+
+
+template<typename T>
+void const_lref_passed_lvalue_int (T const& p) {
+  static_assert(is_same<T, int>::value);
+  static_assert(is_same<decltype(p), int const&>::value);
+  static_assert(is_same<decltype((p)), int const&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void const_lref_passed_lvalue_int (T const& p) {
+  fail_if_not_same<T, int>();
+  fail_if_not_same<decltype(p), int const&>();
+  fail_if_not_same<decltype((p)), int const&>();
+}
+#pragma omp end declare variant
+
+template<typename T>
+void const_lref_passed_lvalue_const_int (T const& p) {
+  static_assert(is_same<T, int>::value);
+  static_assert(is_same<decltype(p), int const&>::value);
+  static_assert(is_same<decltype((p)), int const&>::value);
+}
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void const_lref_passed_lvalue_const_int (T const& p) {
+  fail_if_not_same<T, int>();
+  fail_if_not_same<decltype(p), int const&>();
+  fail_if_not_same<decltype((p)), int const&>();
+}
+#pragma omp end declare variant
+
+void instantiate_const_lref()
+{
+  int lvalue = 0;
+  const_lref_passed_lvalue_int(lvalue);
+  const_lref_passed_lvalue_const_int(static_cast<int const&>(lvalue));
+}
diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C 
b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C
new file mode 100644
index 00000000000..7bd59d07c32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C
@@ -0,0 +1,157 @@
+/* { dg-do compile { target c++11 } } */
+
+/* Test static_assert in variants.  */
+/* Most of the tests in this file are broken and xfailed.
+   See also delim-declare-variant-41.C for a simpler test case for
+   the "base function cannot be resolved" sorry.  */
+
+struct has_value_true { static constexpr bool value = true; };
+
+template<typename T>
+struct always_true {
+  static constexpr bool value = true;
+};
+
+template<typename T>
+void static_assert_in_variant_static_member_uninstantiated (T) { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_static_member_uninstantiated (T)  // { dg-bogus 
"base function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(T::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_static_member_no_param_uninstantiated () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_static_member_no_param_uninstantiated ()  // { 
dg-bogus "base function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(T::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_static_member (T) { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_static_member (T)  // { dg-bogus "base function 
cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(T::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_static_member_no_param () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_static_member_no_param ()  // { dg-bogus "base 
function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(T::value);
+}
+#pragma omp end declare variant
+
+void instantiate_static_assert_in_variant_static_member()
+{
+  static_assert_in_variant_static_member(has_value_true{});
+  static_assert_in_variant_static_member_no_param<has_value_true>();
+}
+
+
+template<typename T>
+void static_assert_in_variant_templ_member_uninstantiated (T) { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_templ_member_uninstantiated (T)  // { dg-bogus 
"base function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(always_true<T>::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_templ_member_no_param_uninstantiated () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_templ_member_no_param_uninstantiated ()  // { 
dg-bogus "base function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(always_true<T>::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_templ_member (T) { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_templ_member (T)  // { dg-bogus "base function 
cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(always_true<T>::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_templ_member_no_param () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_templ_member_no_param ()  // { dg-bogus "base 
function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(always_true<T>::value);
+}
+#pragma omp end declare variant
+
+void instantiate_static_assert_in_variant_templ_member()
+{
+  static_assert_in_variant_templ_member(0);
+  static_assert_in_variant_templ_member_no_param<int>();
+}
+
+
+/* PR118530 affects also the non-delimited form of "declare variant".  */
+template<bool B>
+void static_assert_in_variant_nttp_uninstantiated () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<bool B>
+void static_assert_in_variant_nttp_uninstantiated () {
+  static_assert(B);
+}
+#pragma omp end declare variant
+
+template<bool B>
+void static_assert_in_variant_nttp () { }  // { dg-bogus "no matching function 
for call" "PR118530" { xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<bool B>
+void static_assert_in_variant_nttp () {
+  static_assert(B);
+}
+#pragma omp end declare variant
+
+void instantiate_static_assert_in_variant_nttp()
+{
+  static_assert_in_variant_nttp<true>();
+}
+
+
+template<template<typename> class Templ>
+void static_assert_in_variant_template_template_uninstantiated () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<template<typename> class Templ>
+void static_assert_in_variant_template_template_uninstantiated ()  // { 
dg-bogus "base function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(Templ<void>::value);
+}
+#pragma omp end declare variant
+
+template<template<typename> class Templ>
+void static_assert_in_variant_template_template () { }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<template<typename> class Templ>
+void static_assert_in_variant_template_template ()  // { dg-bogus "base 
function cannot be resolved" "" { xfail *-*-* } }
+{
+  static_assert(Templ<void>::value);
+}
+#pragma omp end declare variant
+
+void instantiate_static_assert_in_variant_template_template()
+{
+  static_assert_in_variant_template_template<always_true>();
+}
diff --git a/libgomp/testsuite/libgomp.c++/delim-declare-variant-1.C 
b/libgomp/testsuite/libgomp.c++/delim-declare-variant-1.C
new file mode 100644
index 00000000000..bf146dd2365
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/delim-declare-variant-1.C
@@ -0,0 +1,29 @@
+/* { dg-additional-options "-foffload=disable" } */
+
+/* Check that variants within a "begin declare variant" directive
+   are attached to the correct overloaded function.  */
+
+int f (int x) { return x; }
+
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+int f (int x) { return -1; }
+#pragma omp end declare variant
+
+int f (int x, int y) { return x * y; }
+
+#pragma omp begin declare variant match (construct={target})
+int f (int x, int y) { return -2; }
+#pragma omp end declare variant
+
+int f (int x, int y, int z) { return x * y * z; }
+
+#pragma omp begin declare variant match (device={kind("host")})
+int f (int x, int y, int z) { return -3; }
+#pragma omp end declare variant
+
+int main (void)
+{
+  if (f (10) != -1) __builtin_abort ();
+  if (f (10, 20) != 200) __builtin_abort ();   /* no match on this one */
+  if (f (10, 20, 30) != -3) __builtin_abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c++/delim-declare-variant-2.C 
b/libgomp/testsuite/libgomp.c++/delim-declare-variant-2.C
new file mode 100644
index 00000000000..6641768c1ef
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/delim-declare-variant-2.C
@@ -0,0 +1,37 @@
+/* Check that "omp begin declare variant" works on methods in a
+   class declaration.  */
+
+class test1 {
+
+ private:
+  int n;
+  static int m;
+
+ public:
+
+  void set_n (int x) { n = x; }
+  int get_n (void) { return n; }
+
+  static void set_m (int x) { m = x; }
+  static int get_m (void) { return m; }
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  int get_n (void) { return n * 2; }
+  static int get_m (void) { return m * 2; }
+  #pragma omp end declare variant
+
+  #pragma omp begin declare variant match (construct={target})
+  int get_n (void) { return this->n * 2; }
+  #pragma omp end declare variant
+};
+
+int test1::m;
+
+int main (void)
+{
+  test1 t1;
+  t1.set_n (10);
+  if (t1.get_n () != 20) __builtin_abort ();
+  test1::set_m (1);
+  if (test1::get_m () != 2) __builtin_abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c++/delim-declare-variant-7.C 
b/libgomp/testsuite/libgomp.c++/delim-declare-variant-7.C
new file mode 100644
index 00000000000..60cc5d8a55b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/delim-declare-variant-7.C
@@ -0,0 +1,39 @@
+/* Check that "omp begin declare variant" works on methods in a template
+   class declaration.  */
+
+template <typename T>
+class test1 {
+
+ private:
+  T n;
+  static T m;
+
+ public:
+
+  void set_n (T x) { n = x; }
+  T get_n (void) { return n; }
+
+  static void set_m (T x) { m = x; }
+  static T get_m (void) { return m; }
+
+  #pragma omp begin declare variant match (implementation={vendor("gnu")})
+  T get_n (void) { return n * 2; }
+  static T get_m (void) { return m * 2; }
+  #pragma omp end declare variant
+
+  #pragma omp begin declare variant match (construct={target})
+  T get_n (void) { return this->n * 2; }
+  #pragma omp end declare variant
+};
+
+template <typename T>
+T test1<T>::m;
+
+int main (void)
+{
+  test1<int> t1;
+  t1.set_n (10);
+  if (t1.get_n () != 20) __builtin_abort ();
+  test1<int>::set_m (1);
+  if (test1<int>::get_m () != 2) __builtin_abort ();
+}
-- 
2.34.1


Reply via email to