diff options
Diffstat (limited to 'gcc')
20 files changed, 1768 insertions, 37 deletions
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc index 3c2ee9ff1ae..c80d2de6deb 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -4602,8 +4602,8 @@ const struct c_omp_directive c_omp_directives[] = { 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. */ @@ -4637,8 +4637,8 @@ const struct c_omp_directive c_omp_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. */ 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) diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C new file mode 100644 index 00000000000..dfeb7c42d08 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-1.C @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-foffload=disable -fdump-tree-gimple" } */ + +/* Check that variants within a "begin declare variant" directive + are attached to the correct overloaded function. */ + +int f (int x) { return x; } + +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +int f (int x) { return -1; } +#pragma omp end declare variant + +int f (int x, int y) { return x * y; } + +#pragma omp begin declare variant match (construct={target}) +int f (int x, int y) { return -2; } +#pragma omp end declare variant + +int f (int x, int y, int z) { return x * y * z; } + +#pragma omp begin declare variant match (device={kind("host")}) +int f (int x, int y, int z) { return -3; } +#pragma omp end declare variant + +int main (void) +{ + if (f (10) != -1) __builtin_abort (); + if (f (10, 20) != 200) __builtin_abort (); /* no match on this one */ + if (f (10, 20, 30) != -3) __builtin_abort (); +} + +/* { dg-final { scan-tree-dump "f\\.ompvariant. \\(10\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "f \\(10, 20\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "f\\.ompvariant. \\(10, 20, 30\\)" "gimple" } } */ + + + + + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C new file mode 100644 index 00000000000..1784e14dfc3 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-2.C @@ -0,0 +1,53 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Check that "omp begin declare variant" works on methods in a + class declaration. */ + +class test1 { + + private: + int n; + static int m; + + public: + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + int get_n (void) { return n * 2; } + static int get_m (void) { return m * 2; } + #pragma omp end declare variant + + #pragma omp begin declare variant match (construct={target}) + int get_n (void) { return this->n * 2; } + #pragma omp end declare variant + + /* The base methods are deliberately declared after the variants in order + to check that the lookup can still find them. */ + void set_n (int x) { n = x; } + int get_n (void) { return n; } + + static void set_m (int x) { m = x; } + static int get_m (void) { return m; } +}; + +int test1::m; + +int main (void) +{ + test1 t1; + t1.set_n (10); + if (t1.get_n () != 20) __builtin_abort (); + test1::set_m (1); + if (test1::get_m () != 2) __builtin_abort (); +} + +/* { dg-final { scan-tree-dump "test1::get_n\\.ompvariant. \\(&t1\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "test1::get_m\\.ompvariant. \\(\\)" "gimple" } } */ + +/* The variants must have internal linkage, not .globl or .weak. */ +/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_ZN5test117get_n\\.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_ZN5test117get_m\\.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_ZN5test117get_n\\.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_ZN5test117get_m\\.ompvariant" } } */ + + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C new file mode 100644 index 00000000000..ccbb01ce794 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-3.C @@ -0,0 +1,37 @@ +/* { dg-do compile } */ + +/* Check that "omp begin declare variant" for class methods outside of the + class declaration gives a sorry. C++ generally does not allow injection + of additional methods into a class outside of its declaration so it is + not clear what this is supposed to do. */ + +class test1 { + + private: + int n; + static int m; + + public: + + void set_n (int x) { n = x; } + int get_n (void) { return n; } + + static void set_m (int x) { m = x; } + static int get_m (void) { return m; } + +}; + +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +int test1::get_n (void) { return n * 2; } /* { dg-message "sorry, unimplemented: cannot handle qualified name for variant function" } */ +static int test1::get_m (void) { return m * 2; } /* { dg-message "sorry, unimplemented: cannot handle qualified name for variant function" } */ +#pragma omp end declare variant + +int main (void) +{ + test1 t1; + t1.set_n (10); + if (t1.get_n () != 20) __builtin_abort (); + test1::set_m (1); + if (test1::get_m () != 2) __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C new file mode 100644 index 00000000000..567cf9c4f8c --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-4.C @@ -0,0 +1,57 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Like c-c++-common/delim-declare-variant-1.c, but with namespaces. */ + +namespace n1 { + +int foo (int a) +{ + return a; +} + +int bar (int x) +{ + return x; +} + +#pragma omp begin declare variant match (construct={target}) +int foo (int a) +{ + return a + 1; +} + +int bar (int x) +{ + return x * 2; +} +#pragma omp end declare variant + +/* Because of the high score value, this variant for "bar" should always be + selected even when the one above also matches. */ +#pragma omp begin declare variant match (implementation={vendor(score(10000):"gnu")}) +int bar (int x) +{ + return x * 4; +} +#pragma omp end declare variant + +} /* namespace n1 */ + +int main (void) +{ + if (n1::foo (42) != 42) __builtin_abort (); + if (n1::bar (3) != 12) __builtin_abort (); +#pragma omp target + { + if (n1::foo (42) != 43) __builtin_abort (); + if (n1::bar (3) != 12) __builtin_abort (); + } +} + +/* { dg-final { scan-tree-dump-times "omp declare variant base \\(foo.ompvariant." 1 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "omp declare variant base \\(bar.ompvariant." 2 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "foo \\(42\\)" 1 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "foo\\.ompvariant. \\(42\\)" 1 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "bar \\(3\\)" 0 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "bar\\.ompvariant. \\(3\\)" 2 "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C new file mode 100644 index 00000000000..4f35d209d25 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-40.C @@ -0,0 +1,51 @@ +// { dg-do compile } + +// Check that variants for a template function are instantiated correctly. +// FIXME: Fails due to PR118530. + +template<typename T> +void f_default_param (T = 42) {} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void f_default_param (T = 42) {} +#pragma omp end declare variant + +template<typename T> +void f_no_param () {} // { dg-bogus "no matching function for call" "PR118530" { xfail *-*-* } } +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void f_no_param () {} +#pragma omp end declare variant + +void instantiate_f() +{ + f_default_param<int>(); + f_no_param<int>(); +} + +template<int> +void nttp () {} // { dg-bogus "no matching function for call" "PR118530" { xfail *-*-* } } +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<int> +void nttp () {} +#pragma omp end declare variant + +void instantiate_nttp() +{ + nttp<42>(); +} + +template<typename> +struct S {}; + +template<template<typename> class Templ> +void templ_templ () {} // { dg-bogus "no matching function for call" "PR118530" { xfail *-*-* } } +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<template<typename> class Templ> +void templ_templ () {} +#pragma omp end declare variant + +void instantiate_templ_templ() +{ + templ_templ<S>(); +} diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C new file mode 100644 index 00000000000..5a352100216 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-41.C @@ -0,0 +1,29 @@ +/* { 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); +} diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C new file mode 100644 index 00000000000..e8db369a67d --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-5.C @@ -0,0 +1,53 @@ +/* { dg-do compile } */ + +/* Check that "omp begin declare variant" for a namespace function outside of + the namespace gives an error. C++ generally does not allow injection of + additional function into a namespace outside of its scope so this is just a + generic error. */ + +namespace n1 { + +int foo (int a) +{ + return a; +} + +int bar (int x) +{ + return x; +} + +} /* namespace n1 */ + + +#pragma omp begin declare variant match (construct={target}) +int n1::foo (int a) /* { dg-message "sorry, unimplemented: cannot handle qualified name for variant function" } */ +{ + return a + 1; +} + +int n1::bar (int x) /* { dg-message "sorry, unimplemented: cannot handle qualified name for variant function" } */ +{ + return x * 2; +} +#pragma omp end declare variant + +/* Because of the high score value, this variant for "bar" should always be + selected even when the one above also matches. */ +#pragma omp begin declare variant match (implementation={vendor(score(10000):"gnu")}) +int n1::bar (int x) /* { dg-message "sorry, unimplemented: cannot handle qualified name for variant function" } */ +{ + return x * 4; +} +#pragma omp end declare variant + +int main (void) +{ + if (n1::foo (42) != 42) __builtin_abort (); + if (n1::bar (3) != 12) __builtin_abort (); +#pragma omp target + { + if (n1::foo (42) != 43) __builtin_abort (); + if (n1::bar (3) != 12) __builtin_abort (); + } +} diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C new file mode 100644 index 00000000000..a958b52d01e --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-50.C @@ -0,0 +1,99 @@ +/* { dg-do compile } */ + +/* Test for restrictions on declare variant functions on virtual functions, + constructors, and destructors. */ + +struct S0 +{ + virtual void f_virtual_before0 () {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + virtual void f_virtual_before0 () {} // { dg-error "declare variant directives are not allowed on virtual functions" } + #pragma omp end declare variant + + virtual void f_virtual_before1 () {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + void f_virtual_before1 () {} // { dg-error "declare variant directives are not allowed on virtual functions" } + #pragma omp end declare variant + + void f_virtual_before2 () {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + virtual void f_virtual_before2 () {} // { dg-error "declare variant directives are not allowed on virtual functions" } + #pragma omp end declare variant + + void f_virtual_before3 () {} + // code elision, no error + #pragma omp begin declare variant match (implementation={vendor("cray")}) + virtual void f_virtual_before3 () {} + #pragma omp end declare variant + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + virtual void f_virtual_after0 () {} // { dg-error "declare variant directives are not allowed on virtual functions" } + #pragma omp end declare variant + virtual void f_virtual_after0 () {} + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + void f_virtual_after1 () {} // { dg-error "declare variant directives are not allowed on virtual functions" } + #pragma omp end declare variant + virtual void f_virtual_after1 () {} + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + virtual void f_virtual_after2 () {} // { dg-error "declare variant directives are not allowed on virtual functions" } + #pragma omp end declare variant + void f_virtual_after2 () {} +}; + +struct S_before { + S_before() {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + S_before() {} // { dg-error "declare variant directives are not allowed on constructors" } + #pragma omp end declare variant + + S_before(int) {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + S_before(int) {} // { dg-error "declare variant directives are not allowed on constructors" } + #pragma omp end declare variant + + S_before(double) {} + // code elision, no error + #pragma omp begin declare variant match (implementation={vendor("cray")}) + S_before(double) {} + #pragma omp end declare variant + + template<typename T> + S_before(T) {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + template<typename T> + S_before(T) {} // { dg-error "declare variant directives are not allowed on constructors" } + #pragma omp end declare variant + + ~S_before() {} + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + ~S_before() {} // { dg-error "declare variant directives are not allowed on destructors" } + #pragma omp end declare variant +}; + +struct S_after { + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + S_after() {} // { dg-error "declare variant directives are not allowed on constructors" } + #pragma omp end declare variant + S_after() {} + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + S_after(int) {} // { dg-error "declare variant directives are not allowed on constructors" } + #pragma omp end declare variant + S_after(int) {} + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + template<typename T> + S_after(T) {} // { dg-error "declare variant directives are not allowed on constructors" } + #pragma omp end declare variant + template<typename T> + S_after(T) {} + + // code elision, no error + #pragma omp begin declare variant match (implementation={vendor("cray")}) + ~S_after() {} + #pragma omp end declare variant + ~S_after() {} +}; + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C new file mode 100644 index 00000000000..129193d39eb --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-51.C @@ -0,0 +1,181 @@ +/* { dg-do compile { target c++11 } } */ + +/* Test delimited declare variant on constexpr, deleted, and defaulted + functions. */ +/* C++11 */ + +/* TODO: add templates cases for constexpr/delete free functions */ + +/* Do we warn for the mismatch? + TBH we probably warn whenever a variant function is constexpr in general. + I can't imagine that we are going to support constant evaluation of a + variant function, realistically the only choice is to always use the base + function if a constant-expression is required. */ +constexpr int freefn_mismatched_constexpr_before0 () { return 0; } +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +int freefn_mismatched_constexpr_before0 () { return 1; } +#pragma omp end declare variant + +int freefn_mismatched_constexpr_before1 () { return 0; } +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +constexpr int freefn_mismatched_constexpr_before1 () { return 1; } +#pragma omp end declare variant + +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +constexpr int freefn_mismatched_constexpr_after0 () { return 1; } +#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; +}; diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C new file mode 100644 index 00000000000..4f4a005c6c7 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-52.C @@ -0,0 +1,24 @@ +/* { dg-do compile { target c++20 } } */ + +/* The procedure that a declare variant directive determined to be a function + variant may not be an immediate function + Declare variant directives may + not be specified for immediate functions. */ +consteval void freefn_consteval_before0 () {} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +consteval void freefn_consteval_before0 () {} // { dg-error "declare variant directives are not allowed on immediate functions" } +#pragma omp end declare variant + +/* Declare variant directives may not be specified for immediate functions. */ +consteval void freefn_consteval_before1 () {} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +void freefn_consteval_before1 () {} // { dg-error "declare variant directives are not allowed on immediate functions" } +#pragma omp end declare variant + +/* The procedure that a declare variant directive determined to be a function + variant may not be an immediate function. */ +void freefn_consteval_before2 () {} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +consteval void freefn_consteval_before2 () {} // { dg-error "declare variant directives are not allowed on immediate functions" } +#pragma omp end declare variant + + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C new file mode 100644 index 00000000000..b5c6e6501fe --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-6.C @@ -0,0 +1,72 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Check "begin declare variant" on template functions. */ + +template <typename T> +T foo (T a) +{ + return a; +} + +template <typename T> +T bar (T x) +{ + return x; +} + +#pragma omp begin declare variant match (construct={target}) +template <typename T1> +T1 foo (T1 a) +{ + return a + 1; +} + +template <typename T1> +T1 bar (T1 x) +{ + return x * 2; +} +#pragma omp end declare variant + +/* Because of the high score value, this variant for "bar" should always be + selected even when the one above also matches. */ +#pragma omp begin declare variant match (implementation={vendor(score(10000):"gnu")}) +template <typename T2> +T2 bar (T2 x) +{ + return x * 4; +} +#pragma omp end declare variant + +int main (void) +{ + if (foo<int> (42) != 42) __builtin_abort (); + if (bar<int> (3) != 12) __builtin_abort (); +#pragma omp target + { + if (foo<int> (42) != 43) __builtin_abort (); + if (bar<int> (3) != 12) __builtin_abort (); + } +} + +/* Make sure all the template functions are instantiated. */ +/* { dg-final { scan-tree-dump "int foo.ompvariant.<int> \\(.*\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "int foo<int> \\(.*\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "int bar.ompvariant.<int> \\(.*\\)" "gimple" } } */ + +/* Make sure the calls are resolved correctly. */ +/* { dg-final { scan-tree-dump-times "foo<int> \\(42\\)" 1 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "foo\\.ompvariant.<int> \\(42\\)" 1 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "bar<int> \\(3\\)" 0 "gimple" } } */ +/* { dg-final { scan-tree-dump-times "bar\\.ompvariant.<int> \\(3\\)" 2 "gimple" } } */ + +/* The variants must have internal linkage, not .globl or .weak. */ +/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_Z15foo.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_Z15bar.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_Z15foo.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_Z15bar.ompvariant" } } */ + + + + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C new file mode 100644 index 00000000000..b24e6c0cb3b --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-7.C @@ -0,0 +1,57 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-gimple" } */ + +/* Check that "omp begin declare variant" works on methods in a template + class declaration. */ + +template <typename T> +class test1 { + + private: + T n; + static T m; + + public: + + void set_n (T x) { n = x; } + T get_n (void) { return n; } + + static void set_m (T x) { m = x; } + static T get_m (void) { return m; } + + #pragma omp begin declare variant match (implementation={vendor("gnu")}) + T get_n (void) { return n * 2; } + static T get_m (void) { return m * 2; } + #pragma omp end declare variant + + #pragma omp begin declare variant match (construct={target}) + T get_n (void) { return this->n * 2; } + #pragma omp end declare variant +}; + +template <typename T> +T test1<T>::m; + +int main (void) +{ + test1<int> t1; + t1.set_n (10); + if (t1.get_n () != 20) __builtin_abort (); + test1<int>::set_m (1); + if (test1<int>::get_m () != 2) __builtin_abort (); +} + +/* Make sure the "declare variant" replacement happens. */ +/* { dg-final { scan-tree-dump "test1<int>::get_n\\.ompvariant. \\(&t1\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "test1<int>::get_m\\.ompvariant. \\(\\)" "gimple" } } */ + +/* Make sure the variant methods are instantiated. */ +/* { dg-final { scan-tree-dump "int test1<int>::get_n\\.ompvariant. \\(.*\\)" "gimple" } } */ +/* { dg-final { scan-tree-dump "int test1<int>::get_m\\.ompvariant. \\(.*\\)" "gimple" } } */ + +/* The variants must have internal linkage, not .globl or .weak. */ +/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_ZN5test1IiE17get_n.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.globl\[ \t\]*_?_ZN5test1IiE17get_m.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_ZN5test1IiE17get_n.ompvariant" } } */ +/* { dg-final { scan-assembler-not "\\.weak\[ \t\]*_?_ZN5test1IiE17get_m.ompvariant" } } */ + diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C new file mode 100644 index 00000000000..ed1e1ae2ddd --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-70.C @@ -0,0 +1,206 @@ +/* { dg-do compile { target c++11 } } */ + +/* Check that the substituted type in variant is the same as the one in the + base. */ + +template<typename T, typename U> +struct is_same { + static constexpr bool value = false; +}; + +template<typename T> +struct is_same<T, T> { + static constexpr bool value = true; +}; + +/* Using static_assert directly in a variant triggers the SCOPE_REF bug noted + in delim-declare-variant-41.C. We'll avoid that by outsourcing the checks + to this function. PR118791 is a different bug that affects also the + non-delimited form of "declare variant". */ +template<typename T, typename U> +void fail_if_not_same() { + static_assert(is_same<T, U>::value); // { dg-bogus "static assertion failed" "PR118791" { xfail *-*-* } } +} + +/* Sanity checks are included in the base function just to be absolutely + certain there were no mistakes made in the tests. They should match the + cases in the variant function exactly. */ + +template<typename T> +void fwdref_passed_lvalue_int (T&& p) { + static_assert(is_same<T, int&>::value); + static_assert(is_same<decltype(p), int&>::value); + static_assert(is_same<decltype((p)), int&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void fwdref_passed_lvalue_int (T&& p) { + fail_if_not_same<T, int&>(); + fail_if_not_same<decltype(p), int&>(); + fail_if_not_same<decltype((p)), int&>(); +} +#pragma omp end declare variant + +template<typename T> +void fwdref_passed_lvalue_const_int (T&& p) { + static_assert(is_same<T, int const&>::value); + static_assert(is_same<decltype(p), int const&>::value); + static_assert(is_same<decltype((p)), int const&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void fwdref_passed_lvalue_const_int (T&& p) { + fail_if_not_same<T, int const&>(); + fail_if_not_same<decltype(p), int const&>(); + fail_if_not_same<decltype((p)), int const&>(); +} +#pragma omp end declare variant + +template<typename T> +void fwdref_passed_rvalue_int (T&& p) { + static_assert(is_same<T, int>::value); + static_assert(is_same<decltype(p), int&&>::value); + static_assert(is_same<decltype((p)), int&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void fwdref_passed_rvalue_int (T&& p) { + fail_if_not_same<T, int>(); + fail_if_not_same<decltype(p), int&&>(); + fail_if_not_same<decltype((p)), int&>(); +} +#pragma omp end declare variant + +template<typename T> +void fwdref_passed_rvalue_const_int (T&& p) { + static_assert(is_same<T, int const>::value); + static_assert(is_same<decltype(p), int const&&>::value); + static_assert(is_same<decltype((p)), int const&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void fwdref_passed_rvalue_const_int (T&& p) { + fail_if_not_same<T, int const>(); + fail_if_not_same<decltype(p), int const&&>(); + fail_if_not_same<decltype((p)), int const&>(); +} +#pragma omp end declare variant + +void instantiate_fwdref() +{ + int lvalue = 0; + fwdref_passed_lvalue_int(lvalue); + fwdref_passed_lvalue_const_int(static_cast<int const&>(lvalue)); + fwdref_passed_rvalue_int(0); + fwdref_passed_rvalue_const_int(static_cast<int const&&>(0)); +} + + + +template<typename T> +void explicit_instantiate_fwdref_with_lvalue_int (T&& p) { + static_assert(is_same<T, int&>::value); + static_assert(is_same<decltype(p), int&>::value); + static_assert(is_same<decltype((p)), int&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void explicit_instantiate_fwdref_with_lvalue_int (T&& p) { + fail_if_not_same<T, int&>(); + fail_if_not_same<decltype(p), int&>(); + fail_if_not_same<decltype((p)), int&>(); +} +#pragma omp end declare variant + +template<typename T> +void explicit_instantiate_fwdref_with_lvalue_const_int (T&& p) { + static_assert(is_same<T, int const&>::value); + static_assert(is_same<decltype(p), int const&>::value); + static_assert(is_same<decltype((p)), int const&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void explicit_instantiate_fwdref_with_lvalue_const_int (T&& p) { + fail_if_not_same<T, int const&>(); + fail_if_not_same<decltype(p), int const&>(); + fail_if_not_same<decltype((p)), int const&>(); +} +#pragma omp end declare variant + +template<typename T> +void explicit_instantiate_fwdref_with_rvalue_int (T&& p) { + static_assert(is_same<T, int&&>::value); + static_assert(is_same<decltype(p), int&&>::value); + static_assert(is_same<decltype((p)), int&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void explicit_instantiate_fwdref_with_rvalue_int (T&& p) { + fail_if_not_same<T, int&&>(); + fail_if_not_same<decltype(p), int&&>(); + fail_if_not_same<decltype((p)), int&>(); +} +#pragma omp end declare variant + +template<typename T> +void explicit_instantiate_fwdref_with_rvalue_const_int (T&& p) { + static_assert(is_same<T, int const&&>::value); + static_assert(is_same<decltype(p), int const&&>::value); + static_assert(is_same<decltype((p)), int const&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void explicit_instantiate_fwdref_with_rvalue_const_int (T&& p) { + fail_if_not_same<T, int const&&>(); + fail_if_not_same<decltype(p), int const&&>(); + fail_if_not_same<decltype((p)), int const&>(); +} +#pragma omp end declare variant + +/* Technically a missuse of a forwarding reference */ +void explicit_instantiate_fwdref() +{ + int lvalue = 0; + explicit_instantiate_fwdref_with_lvalue_int<int&>(lvalue); + explicit_instantiate_fwdref_with_lvalue_const_int<int const&>(static_cast<int const&>(lvalue)); + explicit_instantiate_fwdref_with_rvalue_int<int&&>(0); // { dg-bogus "required from here" "PR118791" { xfail *-*-* } } + explicit_instantiate_fwdref_with_rvalue_const_int<int const&&>(static_cast<int const&&>(0)); // { dg-bogus "required from here" "PR118791" { xfail *-*-* } } +} + + +template<typename T> +void const_lref_passed_lvalue_int (T const& p) { + static_assert(is_same<T, int>::value); + static_assert(is_same<decltype(p), int const&>::value); + static_assert(is_same<decltype((p)), int const&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void const_lref_passed_lvalue_int (T const& p) { + fail_if_not_same<T, int>(); + fail_if_not_same<decltype(p), int const&>(); + fail_if_not_same<decltype((p)), int const&>(); +} +#pragma omp end declare variant + +template<typename T> +void const_lref_passed_lvalue_const_int (T const& p) { + static_assert(is_same<T, int>::value); + static_assert(is_same<decltype(p), int const&>::value); + static_assert(is_same<decltype((p)), int const&>::value); +} +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void const_lref_passed_lvalue_const_int (T const& p) { + fail_if_not_same<T, int>(); + fail_if_not_same<decltype(p), int const&>(); + fail_if_not_same<decltype((p)), int const&>(); +} +#pragma omp end declare variant + +void instantiate_const_lref() +{ + int lvalue = 0; + const_lref_passed_lvalue_int(lvalue); + const_lref_passed_lvalue_const_int(static_cast<int const&>(lvalue)); +} diff --git a/gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C new file mode 100644 index 00000000000..87ec966e415 --- /dev/null +++ b/gcc/testsuite/g++.dg/gomp/delim-declare-variant-71.C @@ -0,0 +1,157 @@ +/* { dg-do compile { target c++11 } } */ + +/* Test static_assert in variants. */ +/* Most of the tests in this file are broken and xfailed. + See also delim-declare-variant-41.C for a simpler test case for + the "base function cannot be resolved" sorry. */ + +struct has_value_true { static constexpr bool value = true; }; + +template<typename T> +struct always_true { + static constexpr bool value = true; +}; + +template<typename T> +void static_assert_in_variant_static_member_uninstantiated (T) { } +#pragma omp begin declare variant match (implementation={vendor("gnu")}) +template<typename T> +void static_assert_in_variant_static_member_uninstantiated (T) +{ + 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>(); +} |
