summaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/cp-tree.h6
-rw-r--r--gcc/cp/decl.cc15
-rw-r--r--gcc/cp/parser.cc642
-rw-r--r--gcc/cp/parser.h12
-rw-r--r--gcc/cp/semantics.cc7
5 files changed, 649 insertions, 33 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c6e284d060c..9b679fcf16a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1946,6 +1946,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 {
@@ -1998,6 +2003,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 c62f0777f5b..c5066dfc60b 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -9075,6 +9075,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 dc26b10f39b..e0c8e0ec8ad 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -273,6 +273,10 @@ static void cp_finalize_oacc_routine
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))
@@ -4561,6 +4565,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. */
@@ -5506,6 +5558,22 @@ cp_parser_translation_unit (cp_parser* parser)
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)
{
@@ -24515,6 +24583,278 @@ 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. 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.
@@ -24731,6 +25071,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,
@@ -24749,6 +25110,20 @@ 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. 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;
}
}
@@ -24826,6 +25201,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
@@ -25075,6 +25471,19 @@ cp_parser_init_declarator (cp_parser* parser,
&& 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;
}
@@ -28326,6 +28735,11 @@ 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. */
+ 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)
@@ -28347,6 +28761,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. */
+ 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. */
@@ -30070,6 +30497,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,
@@ -30080,6 +30529,20 @@ 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)
+ {
+ 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. */
@@ -51566,6 +52029,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
@@ -52227,7 +52704,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) \
@@ -52273,9 +52752,75 @@ 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_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);
}
}
@@ -52288,7 +52833,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);
}
}
@@ -52297,7 +52843,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)
@@ -52319,41 +52866,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);
- 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)
{
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 3a17be993f1..3e7251c38a9 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -233,6 +233,13 @@ struct cp_oacc_routine_data : cp_omp_declare_simd_data {
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 {
@@ -458,6 +465,11 @@ 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 */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3e19a56f51e..0d8981ecabf 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4051,6 +4051,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)