C_OMP_DIR_INFORMATIONAL, false },
{ "begin", "declare", "target", PRAGMA_OMP_BEGIN,
C_OMP_DIR_DECLARATIVE, false },
- /* { "begin", "declare", "variant", PRAGMA_OMP_BEGIN,
- C_OMP_DIR_DECLARATIVE, false }, */
+ { "begin", "declare", "variant", PRAGMA_OMP_BEGIN,
+ C_OMP_DIR_DECLARATIVE, false },
/* 'begin metadirective' is not yet implemented; however,
it is only applicable if an end-directive exists, but
metadirectives are of limited use for declarative directives. */
C_OMP_DIR_INFORMATIONAL, false },
{ "end", "declare", "target", PRAGMA_OMP_END,
C_OMP_DIR_DECLARATIVE, false },
- /* { "end", "declare", "variant", PRAGMA_OMP_END,
- C_OMP_DIR_DECLARATIVE, false }, */
+ { "end", "declare", "variant", PRAGMA_OMP_END,
+ C_OMP_DIR_DECLARATIVE, false },
/* { "end", "metadirective", nullptr, PRAGMA_OMP_END,
C_OMP_DIR_META, false }, */
/* error with at(execution) is C_OMP_DIR_STANDALONE. */
bool attr_syntax;
};
+struct GTY(()) cp_omp_declare_variant_attr {
+ bool attr_syntax;
+ tree selector;
+};
+
/* Global state. */
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;
};
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);
static void check_omp_intervening_code
(cp_parser *);
+static tree omp_start_variant_function
+ (cp_declarator *, tree);
+static int omp_finish_variant_function
+ (cp_parser *, tree, tree, tree, bool, bool);
/* Manifest constants. */
#define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token))
}
}
+/* 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. */
cp_parser_toplevel_declaration (parser);
}
+ /* Register any functions found in "begin declare variant" blocks, whose
+ base function declarations may be found "elsewhere" in the translation
+ unit. */
+ /* FIXME: we could probably just diagnose any leftover functions as an
+ error directly here, all valid cases were supposed to have been
+ caught already. */
+ if (vec_safe_length (parser->omp_begin_declare_variant_map))
+ {
+ unsigned int i;
+ omp_begin_declare_variant_map_entry *e;
+ FOR_EACH_VEC_ELT (*parser->omp_begin_declare_variant_map, i, e)
+ omp_finish_variant_function (parser, e->variant, e->id, e->ctx,
+ false, true);
+ parser->omp_begin_declare_variant_map = NULL;
+ }
+
#if ENABLE_ANALYZER
if (flag_analyzer)
{
}
}
+/* 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. If we're in a module interface,
+ add an additional prefix on the name ("mi" == "module interface"),
+ so that it will not collide with names in the module purview that
+ are defined locally within module interface TUs. Note that the
+ module name mangling of this name happens after this step. */
+ const char *sep
+ = (module_interface_p () ? JOIN_STR "mi" : JOIN_STR);
+ id->u.id.unqualified_name = omp_mangle_variant_name (name, ctx, sep);
+
+ 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. IN_CLASS_P is true for DECLs appearing in a class
+ scope, and if DIAGNOSE_ERROR_P is false do not diagnose errors about
+ failure to match at this time (although other semantic errors when a
+ match is found are always diagnosed). Returns 1 on success, 0 for no
+ match, and -1 for a match with semantic errors. */
+static int
+omp_finish_variant_function (cp_parser *parser, tree decl, tree base_name,
+ tree ctx, bool in_class_p, bool diagnose_error_p)
+{
+ tree match = NULL_TREE;
+ bool is_template = false;
+ tree decl_context = CP_DECL_CONTEXT (decl);
+ tree base_decl;
+
+ /* First find the base_decl in the same scope as decl. If we are in
+ a class scope the parser still points at the scope, otherwise
+ look up base_name in the same namespace as decl appeared in. */
+ if (in_class_p)
+ base_decl = cp_parser_lookup_name_simple (parser, base_name,
+ DECL_SOURCE_LOCATION (decl));
+ else
+ base_decl = lookup_qualified_name (decl_context, base_name);
+
+ if (base_decl == error_mark_node)
+ base_decl = NULL_TREE;
+ if (!base_decl)
+ {
+ if (!diagnose_error_p)
+ return 0;
+ /* We previously rejected non-identifier base function names. */
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "no declaration of base function %qs in this scope",
+ IDENTIFIER_POINTER (base_name));
+ return -1;
+ }
+
+ /* 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 -1;
+ }
+ else if (BASELINK_P (base_decl))
+ {
+ /* Baselink tree nodes do not have a DECL_NAME so we can't use it
+ in the error message. */
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variant function must be in the same scope as the "
+ "base function %qs", IDENTIFIER_POINTER (base_name));
+ return -1;
+ }
+ if (!match)
+ {
+ /* We get here if base_decl doesn't match DECL or have an
+ overload matching DECL. */
+ if (!diagnose_error_p)
+ return 0;
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variant function definition does not match "
+ "declaration of %qE", base_decl);
+ if (TREE_CODE (base_decl) != OVERLOAD)
+ inform (DECL_SOURCE_LOCATION (base_decl), "declared here");
+ return -1;
+ }
+ 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", base_decl);
+ return -1;
+ }
+ 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 -1;
+ }
+ 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 -1;
+ }
+ 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 -1;
+ }
+ 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 -1;
+ }
+
+ /* 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 = build_empty_stmt (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;
+
+ /* Except for variants defined in a module interface, 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. */
+ if (!module_interface_p ())
+ {
+ TREE_PUBLIC (decl) = 0;
+ DECL_COMDAT (decl) = 0;
+ DECL_INTERFACE_KNOWN (decl) = 1;
+ DECL_NOT_REALLY_EXTERN (decl) = 1;
+ }
+ return 1;
+}
+
+/* Called when we have seen a function declaration or definition DECL.
+ OpenMP "begin declare variant" allows variant definitions to precede
+ the declaration of the base function. If DECL is a newly-declared
+ function matching some variant that hasn't been recorded on its base
+ function yet, do that now. */
+
+static void
+omp_maybe_record_variant_base (cp_parser* parser, tree decl)
+{
+ if (vec_safe_length (parser->omp_begin_declare_variant_map))
+ {
+ unsigned int i;
+ omp_begin_declare_variant_map_entry *e;
+ FOR_EACH_VEC_ELT (*parser->omp_begin_declare_variant_map, i, e)
+ {
+ if (DECL_NAME (decl) == e->id
+ && omp_finish_variant_function (parser, e->variant, e->id,
+ e->ctx, false, false))
+ {
+ parser->omp_begin_declare_variant_map->unordered_remove (i);
+ break;
+ }
+ }
+ }
+}
+
/* Declarators [gram.dcl.decl] */
/* Parse an init-declarator.
/* 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,
= 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. Do it now if the base
+ function has already been declared, otherwise save the info. */
+ if (dv_base && decl != error_mark_node
+ && !omp_finish_variant_function (parser, decl, dv_base, dv_ctx,
+ false, false))
+ {
+ omp_begin_declare_variant_map_entry e = { decl, dv_base, dv_ctx };
+ vec_safe_push (parser->omp_begin_declare_variant_map, e);
+ }
+ else if (flag_openmp && !member_p)
+ omp_maybe_record_variant_base (parser, decl);
+
return decl;
}
}
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
&& type_uses_auto (decl_specifiers->type))
*auto_result = strip_declarator_types (TREE_TYPE (decl), declarator);
+ /* Check whether a function declaration might be a base function for a
+ variant previously declared in an OpenMP "begin declare variant" block
+ that has not been recorded yet. Class members are ignored here because
+ they are all processed after parsing the whole class.
+ FIXME: We store these variants in a vector and use a linear search here.
+ We don't expect gazillions of these variant functions to be provided
+ without an earlier declaration, but if it's problematical we could
+ use a hash table instead. */
+ if (flag_openmp
+ && !member_p
+ && function_declarator_p (declarator))
+ omp_maybe_record_variant_base (parser, decl);
+
return decl;
}
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. */
+ vec<omp_begin_declare_variant_map_entry, va_gc> *save_variant_map
+ = parser->omp_begin_declare_variant_map;
+ parser->omp_begin_declare_variant_map = NULL;
/* Start the class. */
if (nested_name_specifier_p)
/* 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. */
+ if (vec_safe_length (parser->omp_begin_declare_variant_map))
+ {
+ unsigned int i;
+ omp_begin_declare_variant_map_entry *e;
+ FOR_EACH_VEC_ELT (*parser->omp_begin_declare_variant_map, i, e)
+ omp_finish_variant_function (parser, e->variant, e->id, e->ctx,
+ true, true);
+ }
+ parser->omp_begin_declare_variant_map = save_variant_map;
+
/* Look for the trailing `}'. */
closing_brace = braces.require_close (parser);
/* Look for trailing attributes to apply to this class. */
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,
/* 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)
+ {
+ omp_begin_declare_variant_map_entry e
+ = { decl, dv_base, dv_ctx };
+ vec_safe_push (parser->omp_begin_declare_variant_map, e);
+ }
+
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* If the next token is a semicolon, consume it. */
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
/* 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) \
= { 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_COMMA))
+ cp_lexer_consume_token (parser->lexer);
+ 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);
}
}
}
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);
}
}
# 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)
p = IDENTIFIER_POINTER (id);
}
if (strcmp (p, "target") == 0)
- cp_lexer_consume_token (parser->lexer);
- else
{
- cp_parser_error (parser, "expected %<target%>");
- cp_parser_skip_to_pragma_eol (parser, pragma_tok);
- return;
+ 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");
+ }
+ }
}
- 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
+ else if (strcmp (p, "variant") == 0)
{
- cp_omp_declare_target_attr
- a = scope_chain->omp_declare_target_attribute->pop ();
- if (a.attr_syntax != in_omp_attribute_pragma)
+ 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
{
- 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");
+ 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;
+ }
}
else if (strcmp (p, "assumes") == 0)
{
tree clauses;
};
+/* Helper data structure for parsing #pragma omp begin declare variant. */
+struct GTY(()) omp_begin_declare_variant_map_entry {
+ tree variant; /* The variant decl. */
+ tree id; /* Name of base function. */
+ tree ctx; /* The context selector associated with the variant. */
+};
+
/* The cp_parser structure represents the C++ parser. */
struct GTY(()) cp_parser {
outside that file. */
struct omp_metadirective_parse_data * GTY((skip))
omp_metadirective_state;
+
+ /* Recorded information about functions in OpenMP "begin declare variant"
+ constructs that still need to be registered with their base functions. */
+ vec<omp_begin_declare_variant_map_entry, va_gc> *
+ omp_begin_declare_variant_map;
};
/* In parser.cc */
"#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)
--- /dev/null
+/* { 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" } } */
+
+
+
+
+
--- /dev/null
+/* { 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" } } */
+
+
--- /dev/null
+/* { 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 ();
+}
+
--- /dev/null
+/* { 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" } } */
--- /dev/null
+// { 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>();
+}
--- /dev/null
+/* { dg-do compile { target c++11 } } */
+
+template<typename T, typename U>
+class is_same {
+ public:
+ static constexpr bool value = false;
+};
+
+template<typename T>
+class is_same<T, T> {
+ public:
+ 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&&) {
+ static_assert(is_same<T, int>::value);
+}
+#pragma omp end declare variant
+
+int main()
+{
+ int lvalue = 42;
+ fn(0);
+}
--- /dev/null
+/* { 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 ();
+ }
+}
--- /dev/null
+/* { 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() {}
+};
+
--- /dev/null
+/* { 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; }
+#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; }
+#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 "declare variant directives are not allowed on deleted functions" }
+#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;
+};
--- /dev/null
+/* { 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
+
+
--- /dev/null
+/* { 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" } } */
+
+
+
+
--- /dev/null
+/* { 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" } } */
+
--- /dev/null
+/* { 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));
+}
--- /dev/null
+/* { 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)
+{
+ 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 ()
+{
+ 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)
+{
+ static_assert(T::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_static_member_no_param () { } // { dg-bogus "no matching function for call" "" { xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_static_member_no_param ()
+{
+ 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)
+{
+ 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 ()
+{
+ 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)
+{
+ static_assert(always_true<T>::value);
+}
+#pragma omp end declare variant
+
+template<typename T>
+void static_assert_in_variant_templ_member_no_param () { } // { dg-bogus "no matching function for call" "" { xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<typename T>
+void static_assert_in_variant_templ_member_no_param ()
+{
+ 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 ()
+{
+ static_assert(Templ<void>::value);
+}
+#pragma omp end declare variant
+
+template<template<typename> class Templ>
+void static_assert_in_variant_template_template () { } // { dg-bogus "no matching function for call" "" { xfail *-*-* } }
+#pragma omp begin declare variant match (implementation={vendor("gnu")})
+template<template<typename> class Templ>
+void static_assert_in_variant_template_template ()
+{
+ 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>();
+}
--- /dev/null
+// Test that "begin declare variant" in a module interface is
+// visible to things that import the module.
+
+// { dg-additional-sources "bdv_module1_main.C" }
+// { dg-additional-options "-fmodules" }
+
+export module bdv_module1;
+
+export int
+test ()
+{
+ return 0;
+}
+
+#if _OPENMP
+#pragma omp begin declare variant match(construct={parallel})
+export int
+test ()
+{
+ return 1;
+}
+#pragma omp end declare variant
+#endif
--- /dev/null
+// { dg-skip-if "" { *-*-* } }
+// Built with bdv_module1.C
+
+import bdv_module1;
+
+int
+main ()
+{
+ if (test () != 0)
+ __builtin_abort ();
+ #pragma omp parallel if(0)
+ {
+ if (test () != 1)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+// Test that "begin declare variant" in a module implementation unit is
+// visible only in that unit.
+
+// { dg-additional-sources "bdv_module2_impl.C bdv_module2_main.C" }
+// { dg-additional-options "-fmodules" }
+
+export module bdv_module2;
+
+export int
+test ()
+{
+ return 0;
+}
+
+export void doit ();
--- /dev/null
+// { dg-skip-if "" { *-*-* } }
+// Built with bdv_module2.C
+
+module bdv_module2;
+
+#if _OPENMP
+#pragma omp begin declare variant match(construct={teams})
+int
+test ()
+{
+ return -1;
+}
+#pragma omp end declare variant
+#endif
+
+void
+doit ()
+{
+ if (test () != 0)
+ __builtin_abort ();
+ #pragma omp teams
+ {
+ if (test () != -1)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+// { dg-skip-if "" { *-*-* } }
+// Built with bdv_module2.C
+
+import bdv_module2;
+
+int
+main ()
+{
+ // Calls to test from doit() should invoke the omp teams variant
+ // present in the TU where it is defined.
+ doit ();
+ // Calls to test from here shouldn't.
+ if (test () != 0)
+ __builtin_abort ();
+ #pragma omp teams
+ {
+ if (test () != 0)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+// Test that "begin declare variant" in a module interface is
+// visible to things that import the module, and that it works in
+// conjunction with additional "begin declare variant"s local
+// to a module implementation TU.
+
+// { dg-additional-sources "bdv_module3_impl.C bdv_module3_main.C" }
+// { dg-additional-options "-fmodules" }
+
+export module bdv_module3;
+
+export int
+test ()
+{
+ return 0;
+}
+
+#if _OPENMP
+#pragma omp begin declare variant match(construct={parallel})
+export int
+test ()
+{
+ return 1;
+}
+#pragma omp end declare variant
+#endif
+
+export void doit ();
--- /dev/null
+// { dg-skip-if "" { *-*-* } }
+// Built with bdv_module3.C
+
+module bdv_module3;
+
+#if _OPENMP
+#pragma omp begin declare variant match(construct={teams})
+int
+test ()
+{
+ return -1;
+}
+#pragma omp end declare variant
+#endif
+
+void
+doit ()
+{
+ if (test () != 0)
+ __builtin_abort ();
+ #pragma omp teams
+ {
+ if (test () != -1)
+ __builtin_abort ();
+ }
+ #pragma omp parallel if(0)
+ {
+ if (test () != 1)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+// { dg-skip-if "" { *-*-* } }
+// Built with bdv_module3.C
+
+import bdv_module3;
+
+int
+main ()
+{
+ // Calls to test from doit() should invoke the omp teams variant
+ // present in the TU where it is defined.
+ doit ();
+ // Calls to test from here shouldn't.
+ if (test () != 0)
+ __builtin_abort ();
+ #pragma omp teams
+ {
+ if (test () != 0)
+ __builtin_abort ();
+ }
+ #pragma omp parallel if(0)
+ {
+ if (test () != 1)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+/* { 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 ();
+}
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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 ();
+}