Hi! This patch adds support for declare simd and declare variant in attribute syntax. Either in attribute-specifier-seq at the start of declaration, in that case it has similar restriction to pragma-syntax, that there is a single function declaration/definition in the declaration, rather than variable declaration or more than one function declarations or mix of function and variable declarations. Or after the declarator id, in that case it applies just to the single function declaration and the same declaration can have multiple such attributes. Or both.
Furthermore, cp_parser_statement has been adjusted so that it doesn't accept [[omp::directive (parallel)]] etc. before statements that don't take attributes at all, or where those attributes don't appertain to the statement but something else (e.g. to label, using directive, declaration, etc.). Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk. 2021-08-10 Jakub Jelinek <ja...@redhat.com> gcc/cp/ * parser.h (struct cp_omp_declare_simd_data): Remove in_omp_attribute_pragma and clauses members, add loc and attribs. (struct cp_oacc_routine_data): Remove loc member, add clauses member. * parser.c (cp_finalize_omp_declare_simd): New function. (cp_parser_handle_statement_omp_attributes): Mention in function comment the function is used also for attribute-declaration. (cp_parser_handle_directive_omp_attributes): New function. (cp_parser_statement): Don't call cp_parser_handle_statement_omp_attributes if statement doesn't have attribute-specifier-seq at the beginning at all or if if those attributes don't appertain to the statement. (cp_parser_simple_declaration): Call cp_parser_handle_directive_omp_attributes and cp_finalize_omp_declare_simd. (cp_parser_explicit_instantiation): Likewise. (cp_parser_init_declarator): Initialize prefix_attributes only after parsing declarators. (cp_parser_direct_declarator): Call cp_parser_handle_directive_omp_attributes and cp_finalize_omp_declare_simd. (cp_parser_member_declaration): Likewise. (cp_parser_single_declaration): Likewise. (cp_parser_omp_declare_simd): Don't initialize data.in_omp_attribute_pragma, instead initialize data.attribs[0] and data.attribs[1]. (cp_finish_omp_declare_variant): Remove in_omp_attribute_pragma argument, instead use parser->lexer->in_omp_attribute_pragma. (cp_parser_late_parsing_omp_declare_simd): Adjust cp_finish_omp_declare_variant caller. Handle attribute-syntax declare simd/variant. gcc/testsuite/ * g++.dg/gomp/attrs-1.C (bar): Add missing semicolon after [[omp::directive (threadprivate (t2))]]. Add tests with if/while/switch after parallel in attribute syntax. (corge): Add missing omp:: before directive. * g++.dg/gomp/attrs-2.C (bar): Add missing semicolon after [[omp::directive (threadprivate (t2))]]. * g++.dg/gomp/attrs-10.C: New test. * g++.dg/gomp/attrs-11.C: New test. --- gcc/cp/parser.h.jj 2021-08-09 11:36:38.449390046 +0200 +++ gcc/cp/parser.h 2021-08-09 15:14:20.375843582 +0200 @@ -216,15 +216,14 @@ struct cp_omp_declare_simd_data { bool error_seen; /* Set if error has been reported. */ bool fndecl_seen; /* Set if one fn decl/definition has been seen already. */ bool variant_p; /* Set for #pragma omp declare variant. */ - bool in_omp_attribute_pragma; /* True if declare simd/variant comes from - C++11 attribute rather than pragma. */ + location_t loc; vec<cp_token_cache_ptr> tokens; - tree clauses; + tree *attribs[2]; }; /* Helper data structure for parsing #pragma acc routine. */ struct cp_oacc_routine_data : cp_omp_declare_simd_data { - location_t loc; + tree clauses; }; /* The cp_parser structure represents the C++ parser. */ --- gcc/cp/parser.c.jj 2021-08-09 11:36:38.412390555 +0200 +++ gcc/cp/parser.c 2021-08-09 20:47:06.620792815 +0200 @@ -1440,6 +1440,24 @@ cp_finalize_omp_declare_simd (cp_parser } } +/* Similarly, but for use in declaration parsing functions + which call cp_parser_handle_directive_omp_attributes. */ + +static inline void +cp_finalize_omp_declare_simd (cp_parser *parser, cp_omp_declare_simd_data *data) +{ + if (parser->omp_declare_simd != data) + return; + + if (!parser->omp_declare_simd->error_seen + && !parser->omp_declare_simd->fndecl_seen) + error_at (parser->omp_declare_simd->loc, + "%<declare %s%> directive not immediately followed by " + "function declaration or definition", + parser->omp_declare_simd->variant_p ? "variant" : "simd"); + parser->omp_declare_simd = NULL; +} + /* Diagnose if #pragma acc routine isn't followed immediately by function declaration or definition. */ @@ -11661,7 +11679,7 @@ struct cp_omp_attribute_data }; /* Handle omp::directive and omp::sequence attributes in ATTRS - (if any) at the start of a statement. */ + (if any) at the start of a statement or in attribute-declaration. */ static tree cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs) @@ -11858,6 +11876,98 @@ cp_parser_handle_statement_omp_attribute return attrs; } +/* Handle omp::directive and omp::sequence attributes in *PATTRS + (if any) at the start or after declaration-id of a declaration. */ + +static void +cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs, + cp_omp_declare_simd_data *data, + bool start) +{ + if (!flag_openmp && !flag_openmp_simd) + return; + + int cnt = 0; + bool bad = false; + bool variant_p = false; + location_t loc = UNKNOWN_LOCATION; + for (tree pa = *pattrs; pa; pa = TREE_CHAIN (pa)) + if (get_attribute_namespace (pa) == omp_identifier + && is_attribute_p ("directive", get_attribute_name (pa))) + { + for (tree a = TREE_VALUE (pa); a; a = TREE_CHAIN (a)) + { + tree d = TREE_VALUE (a); + gcc_assert (TREE_CODE (d) == DEFERRED_PARSE); + cp_token *first = DEFPARSE_TOKENS (d)->first; + cp_token *last = DEFPARSE_TOKENS (d)->last; + const char *directive[3] = {}; + for (int i = 0; i < 3; i++) + { + tree id = NULL_TREE; + if (first + i == last) + break; + if (first[i].type == CPP_NAME) + id = first[i].u.value; + else if (first[i].type == CPP_KEYWORD) + id = ridpointers[(int) first[i].keyword]; + else + break; + directive[i] = IDENTIFIER_POINTER (id); + } + const c_omp_directive *dir = NULL; + if (directive[0]) + dir = c_omp_categorize_directive (directive[0], directive[1], + directive[2]); + if (dir == NULL) + continue; + if (dir->id == PRAGMA_OMP_DECLARE + && (strcmp (directive[1], "simd") == 0 + || strcmp (directive[1], "variant") == 0)) + { + if (cnt++ == 0) + { + variant_p = strcmp (directive[1], "variant") == 0; + loc = first->location; + } + if (start && parser->omp_declare_simd && !bad) + { + error_at (first->location, + "mixing OpenMP directives with attribute and " + "pragma syntax on the same declaration"); + bad = true; + } + } + } + } + + if (bad) + { + for (tree *pa = pattrs; *pa; ) + if (get_attribute_namespace (*pa) == omp_identifier + && is_attribute_p ("directive", get_attribute_name (*pa))) + *pa = TREE_CHAIN (*pa); + else + pa = &TREE_CHAIN (*pa); + return; + } + if (cnt == 0) + return; + + if (parser->omp_declare_simd == NULL) + { + data->error_seen = false; + data->fndecl_seen = false; + data->variant_p = variant_p; + data->loc = loc; + data->tokens = vNULL; + data->attribs[0] = NULL; + data->attribs[1] = NULL; + parser->omp_declare_simd = data; + } + parser->omp_declare_simd->attribs[!start] = pattrs; +} + /* Parse a statement. statement: @@ -11935,12 +12045,57 @@ cp_parser_statement (cp_parser* parser, } has_std_attrs = cp_lexer_peek_token (parser->lexer) != token; + /* Peek at the next token. */ + token = cp_lexer_peek_token (parser->lexer); + bool omp_attrs_forbidden_p; + omp_attrs_forbidden_p = parser->omp_attrs_forbidden_p; + if (std_attrs && (flag_openmp || flag_openmp_simd)) - std_attrs = cp_parser_handle_statement_omp_attributes (parser, std_attrs); + { + bool handle_omp_attribs = false; + if (token->type == CPP_KEYWORD) + switch (token->keyword) + { + case RID_IF: + case RID_SWITCH: + case RID_WHILE: + case RID_DO: + case RID_FOR: + case RID_BREAK: + case RID_CONTINUE: + case RID_RETURN: + case RID_CO_RETURN: + case RID_GOTO: + case RID_AT_TRY: + case RID_AT_CATCH: + case RID_AT_FINALLY: + case RID_AT_SYNCHRONIZED: + case RID_AT_THROW: + case RID_TRY: + case RID_TRANSACTION_ATOMIC: + case RID_TRANSACTION_RELAXED: + case RID_SYNCHRONIZED: + case RID_ATOMIC_NOEXCEPT: + case RID_ATOMIC_CANCEL: + case RID_TRANSACTION_CANCEL: + handle_omp_attribs = true; + break; + default: + break; + } + else if (token->type == CPP_SEMICOLON + || token->type == CPP_OPEN_BRACE + || token->type == CPP_PRAGMA) + handle_omp_attribs = true; + if (handle_omp_attribs) + { + std_attrs = cp_parser_handle_statement_omp_attributes (parser, + std_attrs); + token = cp_lexer_peek_token (parser->lexer); + } + } parser->omp_attrs_forbidden_p = false; - /* Peek at the next token. */ - token = cp_lexer_peek_token (parser->lexer); /* Remember the location of the first token in the statement. */ cp_token *statement_token = token; statement_location = token->location; @@ -12058,6 +12213,7 @@ cp_parser_statement (cp_parser* parser, a statement all its own. */ else if (token->type == CPP_PRAGMA) { + do_pragma:; cp_lexer *lexer = parser->lexer; bool do_restart = false; /* Only certain OpenMP pragmas are attached to statements, and thus @@ -12120,7 +12276,46 @@ cp_parser_statement (cp_parser* parser, return; /* It didn't work, restore the post-attribute position. */ if (has_std_attrs) - cp_lexer_set_token_position (parser->lexer, statement_token); + { + cp_lexer_set_token_position (parser->lexer, statement_token); + if (flag_openmp || flag_openmp_simd) + { + size_t i = 1; + bool handle_omp_attribs = true; + while (cp_lexer_peek_nth_token (parser->lexer, i)->keyword + == RID_EXTENSION) + i++; + switch (cp_lexer_peek_nth_token (parser->lexer, i)->keyword) + { + case RID_ASM: + case RID_NAMESPACE: + case RID_USING: + case RID_LABEL: + case RID_STATIC_ASSERT: + /* Don't handle OpenMP attribs on keywords that + always start a declaration statement but don't + accept attribute before it and therefore + the tentative cp_parser_declaration_statement + fails to parse because of that. */ + handle_omp_attribs = false; + break; + default: + break; + } + + if (handle_omp_attribs) + { + parser->omp_attrs_forbidden_p = omp_attrs_forbidden_p; + std_attrs + = cp_parser_handle_statement_omp_attributes + (parser, std_attrs); + parser->omp_attrs_forbidden_p = false; + token = cp_lexer_peek_token (parser->lexer); + if (token->type == CPP_PRAGMA) + goto do_pragma; + } + } + } } /* All preceding labels have been parsed at this point. */ if (loc_after_labels != NULL) @@ -14770,6 +14965,12 @@ cp_parser_simple_declaration (cp_parser* /* We no longer need to defer access checks. */ stop_deferring_access_checks (); + cp_omp_declare_simd_data odsd; + if (decl_specifiers.attributes && (flag_openmp || flag_openmp_simd)) + cp_parser_handle_directive_omp_attributes (parser, + &decl_specifiers.attributes, + &odsd, true); + /* In a block scope, a valid declaration must always have a decl-specifier-seq. By not trying to parse declarators, we can resolve the declaration/expression ambiguity more quickly. */ @@ -14962,6 +15163,7 @@ cp_parser_simple_declaration (cp_parser* else { pop_deferring_access_checks (); + cp_finalize_omp_declare_simd (parser, &odsd); return; } } @@ -15042,6 +15244,7 @@ cp_parser_simple_declaration (cp_parser* done: pop_deferring_access_checks (); + cp_finalize_omp_declare_simd (parser, &odsd); } /* Helper of cp_parser_simple_declaration, parse a decomposition declaration. @@ -18659,6 +18862,13 @@ cp_parser_explicit_instantiation (cp_par CP_PARSER_FLAGS_OPTIONAL, &decl_specifiers, &declares_class_or_enum); + + cp_omp_declare_simd_data odsd; + if (decl_specifiers.attributes && (flag_openmp || flag_openmp_simd)) + cp_parser_handle_directive_omp_attributes (parser, + &decl_specifiers.attributes, + &odsd, true); + /* If there was exactly one decl-specifier, and it declared a class, and there's no declarator, then we have an explicit type instantiation. */ @@ -18727,6 +18937,8 @@ cp_parser_explicit_instantiation (cp_par cp_parser_consume_semicolon_at_end_of_statement (parser); timevar_pop (TV_TEMPLATE_INST); + + cp_finalize_omp_declare_simd (parser, &odsd); } /* Parse an explicit-specialization. @@ -21964,10 +22176,6 @@ cp_parser_init_declarator (cp_parser* pa if (decl_spec_seq_has_spec_p (decl_specifiers, ds_consteval)) flags |= CP_PARSER_FLAGS_CONSTEVAL; - /* Gather the attributes that were provided with the - decl-specifiers. */ - prefix_attributes = decl_specifiers->attributes; - /* Assume that this is not the declarator for a function definition. */ if (function_definition_p) @@ -22031,6 +22239,10 @@ cp_parser_init_declarator (cp_parser* pa else asm_specification = NULL_TREE; + /* Gather the attributes that were provided with the + decl-specifiers. */ + prefix_attributes = decl_specifiers->attributes; + /* Look for attributes. */ attributes_start_token = cp_lexer_peek_token (parser->lexer); attributes = cp_parser_attributes_opt (parser); @@ -22679,13 +22891,27 @@ cp_parser_direct_declarator (cp_parser* attrs = cp_parser_std_attribute_spec_seq (parser); + cp_omp_declare_simd_data odsd; + if ((flag_openmp || flag_openmp_simd) + && declarator + && declarator->std_attributes + && declarator->kind == cdk_id) + { + tree *pa = &declarator->std_attributes; + cp_parser_handle_directive_omp_attributes (parser, pa, + &odsd, false); + } + /* In here, we handle cases where attribute is used after the function declaration. For example: void func (int x) __attribute__((vector(..))); */ tree gnu_attrs = NULL_TREE; tree requires_clause = NULL_TREE; - late_return = (cp_parser_late_return_type_opt - (parser, declarator, requires_clause)); + late_return + = cp_parser_late_return_type_opt (parser, declarator, + requires_clause); + + cp_finalize_omp_declare_simd (parser, &odsd); /* Parse the virt-specifier-seq. */ virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); @@ -26435,6 +26661,13 @@ cp_parser_member_declaration (cp_parser* | CP_PARSER_FLAGS_TYPENAME_OPTIONAL), &decl_specifiers, &declares_class_or_enum); + + cp_omp_declare_simd_data odsd; + if (decl_specifiers.attributes && (flag_openmp || flag_openmp_simd)) + cp_parser_handle_directive_omp_attributes (parser, + &decl_specifiers.attributes, + &odsd, true); + /* Check for an invalid type-name. */ if (!decl_specifiers.any_type_specifiers_p && cp_parser_parse_and_diagnose_invalid_type_name (parser)) @@ -26554,6 +26787,10 @@ cp_parser_member_declaration (cp_parser* being declared. */ prefix_attributes = decl_specifiers.attributes; decl_specifiers.attributes = NULL_TREE; + if (parser->omp_declare_simd + && (parser->omp_declare_simd->attribs[0] + == &decl_specifiers.attributes)) + parser->omp_declare_simd->attribs[0] = &prefix_attributes; /* See if these declarations will be friends. */ friend_p = cp_parser_friend_p (&decl_specifiers); @@ -26942,6 +27179,7 @@ cp_parser_member_declaration (cp_parser* cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); out: parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; + cp_finalize_omp_declare_simd (parser, &odsd); } /* Parse a pure-specifier. @@ -31067,6 +31305,13 @@ cp_parser_single_declaration (cp_parser* | CP_PARSER_FLAGS_TYPENAME_OPTIONAL), &decl_specifiers, &declares_class_or_enum); + + cp_omp_declare_simd_data odsd; + if (decl_specifiers.attributes && (flag_openmp || flag_openmp_simd)) + cp_parser_handle_directive_omp_attributes (parser, + &decl_specifiers.attributes, + &odsd, true); + if (friend_p) *friend_p = cp_parser_friend_p (&decl_specifiers); @@ -31195,6 +31440,8 @@ cp_parser_single_declaration (cp_parser* parser->qualifying_scope = NULL_TREE; parser->object_scope = NULL_TREE; + cp_finalize_omp_declare_simd (parser, &odsd); + return decl; } @@ -43546,9 +43793,10 @@ cp_parser_omp_declare_simd (cp_parser *p data.error_seen = false; data.fndecl_seen = false; data.variant_p = variant_p; - data.in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma; data.tokens = vNULL; - data.clauses = NULL_TREE; + data.attribs[0] = NULL; + data.attribs[1] = NULL; + data.loc = UNKNOWN_LOCATION; /* It is safe to take the address of a local variable; it will only be used while this scope is live. */ parser->omp_declare_simd = &data; @@ -43985,7 +44233,7 @@ cp_parser_omp_context_selector_specifica static tree cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, - tree attrs, bool in_omp_attribute_pragma) + tree attrs) { matching_parens parens; if (!parens.require_open (parser)) @@ -44044,7 +44292,7 @@ cp_finish_omp_declare_variant (cp_parser location_t varid_loc = make_location (caret_loc, start_loc, finish_loc); /* For now only in C++ attributes, do it always for OpenMP 5.1. */ - if (in_omp_attribute_pragma + if (parser->lexer->in_omp_attribute_pragma && cp_lexer_next_token_is (parser->lexer, CPP_COMMA) && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) cp_lexer_consume_token (parser->lexer); @@ -44121,11 +44369,10 @@ cp_parser_late_parsing_omp_declare_simd cp_lexer_consume_token (parser->lexer); if (strcmp (kind, "simd") == 0) { - /* For now only in C++ attributes, do it always for OpenMP 5.1. */ - if (data->in_omp_attribute_pragma - && cp_lexer_next_token_is (parser->lexer, CPP_COMMA) + /* For now only in C++ attributes, do it always for OpenMP 5.1. + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) - cp_lexer_consume_token (parser->lexer); + cp_lexer_consume_token (parser->lexer); */ cl = cp_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK, "#pragma omp declare simd", @@ -44142,12 +44389,142 @@ cp_parser_late_parsing_omp_declare_simd { gcc_assert (strcmp (kind, "variant") == 0); attrs - = cp_finish_omp_declare_variant (parser, pragma_tok, attrs, - data->in_omp_attribute_pragma); + = cp_finish_omp_declare_variant (parser, pragma_tok, attrs); } cp_parser_pop_lexer (parser); } + cp_lexer *lexer = NULL; + for (int i = 0; i < 2; i++) + { + if (data->attribs[i] == NULL) + continue; + for (tree *pa = data->attribs[i]; *pa; ) + if (get_attribute_namespace (*pa) == omp_identifier + && is_attribute_p ("directive", get_attribute_name (*pa))) + { + for (tree a = TREE_VALUE (*pa); a; a = TREE_CHAIN (a)) + { + tree d = TREE_VALUE (a); + gcc_assert (TREE_CODE (d) == DEFERRED_PARSE); + cp_token *first = DEFPARSE_TOKENS (d)->first; + cp_token *last = DEFPARSE_TOKENS (d)->last; + const char *directive[3] = {}; + for (int j = 0; j < 3; j++) + { + tree id = NULL_TREE; + if (first + j == last) + break; + if (first[j].type == CPP_NAME) + id = first[j].u.value; + else if (first[j].type == CPP_KEYWORD) + id = ridpointers[(int) first[j].keyword]; + else + break; + directive[j] = IDENTIFIER_POINTER (id); + } + const c_omp_directive *dir = NULL; + if (directive[0]) + dir = c_omp_categorize_directive (directive[0], directive[1], + directive[2]); + if (dir == NULL) + { + error_at (first->location, + "unknown OpenMP directive name in " + "%<omp::directive%> attribute argument"); + continue; + } + if (dir->id != PRAGMA_OMP_DECLARE + || (strcmp (directive[1], "simd") != 0 + && strcmp (directive[1], "variant") != 0)) + { + error_at (first->location, + "OpenMP directive other than %<declare simd%> " + "or %<declare variant%> appertains to a " + "declaration"); + continue; + } + + if (!flag_openmp && strcmp (directive[1], "simd") != 0) + continue; + if (lexer == NULL) + { + lexer = cp_lexer_alloc (); + lexer->debugging_p = parser->lexer->debugging_p; + } + vec_safe_reserve (lexer->buffer, (last - first) + 2); + cp_token tok = {}; + tok.type = CPP_PRAGMA; + tok.keyword = RID_MAX; + tok.u.value = build_int_cst (NULL, PRAGMA_OMP_DECLARE); + tok.location = first->location; + lexer->buffer->quick_push (tok); + while (++first < last) + lexer->buffer->quick_push (*first); + tok = {}; + tok.type = CPP_PRAGMA_EOL; + tok.keyword = RID_MAX; + tok.location = last->location; + lexer->buffer->quick_push (tok); + tok = {}; + tok.type = CPP_EOF; + tok.keyword = RID_MAX; + tok.location = last->location; + lexer->buffer->quick_push (tok); + lexer->next = parser->lexer; + lexer->next_token = lexer->buffer->address (); + lexer->last_token = lexer->next_token + + lexer->buffer->length () + - 1; + lexer->in_omp_attribute_pragma = true; + parser->lexer = lexer; + /* Move the current source position to that of the first token + in the new lexer. */ + cp_lexer_set_source_position_from_token (lexer->next_token); + + cp_token *pragma_tok = cp_lexer_consume_token (parser->lexer); + tree id = cp_lexer_peek_token (parser->lexer)->u.value; + const char *kind = IDENTIFIER_POINTER (id); + cp_lexer_consume_token (parser->lexer); + + tree c, cl; + if (strcmp (kind, "simd") == 0) + { + if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) + cp_lexer_consume_token (parser->lexer); + + omp_clause_mask mask = OMP_DECLARE_SIMD_CLAUSE_MASK; + cl = cp_parser_omp_all_clauses (parser, mask, + "#pragma omp declare simd", + pragma_tok); + if (cl) + cl = tree_cons (NULL_TREE, cl, NULL_TREE); + c = build_tree_list (get_identifier ("omp declare simd"), + cl); + TREE_CHAIN (c) = attrs; + if (processing_template_decl) + ATTR_IS_DEPENDENT (c) = 1; + attrs = c; + } + else + { + gcc_assert (strcmp (kind, "variant") == 0); + attrs + = cp_finish_omp_declare_variant (parser, pragma_tok, + attrs); + } + gcc_assert (parser->lexer != lexer); + vec_safe_truncate (lexer->buffer, 0); + } + *pa = TREE_CHAIN (*pa); + } + else + pa = &TREE_CHAIN (*pa); + } + if (lexer) + cp_lexer_destroy (lexer); + data->fndecl_seen = true; return attrs; } --- gcc/testsuite/g++.dg/gomp/attrs-1.C.jj 2021-07-27 09:47:44.221778658 +0200 +++ gcc/testsuite/g++.dg/gomp/attrs-1.C 2021-08-09 17:42:20.227904251 +0200 @@ -501,10 +501,18 @@ bar (int d, int m, int i1, int i2, int i } } extern int t2; - [[omp::directive (threadprivate (t2))]] + [[omp::directive (threadprivate (t2))]]; extern int t2; [[omp::directive (declare reduction (dr: int: omp_out += omp_in) initializer (omp_priv = 0))]] ; + [[omp::directive (parallel)]] + if (0) + ; + [[omp::directive (parallel)]] + while (0) + ; + [[omp::directive (parallel)]] + switch (0) { case 1: break; default: break; } } void corge1 (); @@ -521,7 +529,7 @@ corge () omp::directive (declare simd simdlen(8) notinbranch)]] extern int corge3 (int l, int *p); [[omp::directive (declare simd simdlen(4) linear(l) aligned(p:4) uniform(p) inbranch), - directive (declare simd simdlen(8) notinbranch)]] + omp::directive (declare simd simdlen(8) notinbranch)]] extern int corge4 (int l, int *p); [[omp::sequence (directive (declare simd simdlen(4) linear(l) aligned(p:4) uniform(p) inbranch), omp::directive (declare simd simdlen(8) notinbranch))]] --- gcc/testsuite/g++.dg/gomp/attrs-2.C.jj 2021-07-27 09:47:44.222778645 +0200 +++ gcc/testsuite/g++.dg/gomp/attrs-2.C 2021-08-09 16:11:37.310671705 +0200 @@ -501,7 +501,7 @@ bar (int d, int m, int i1, int i2, int i } } extern int t2; - [[omp::directive (threadprivate (t2))]] + [[omp::directive (threadprivate (t2))]]; extern int t2; [[omp::directive (declare reduction (dr: int: omp_out += omp_in),initializer (omp_priv = 0))]] ; --- gcc/testsuite/g++.dg/gomp/attrs-10.C.jj 2021-08-09 15:14:20.375843582 +0200 +++ gcc/testsuite/g++.dg/gomp/attrs-10.C 2021-08-09 20:49:46.058619042 +0200 @@ -0,0 +1,240 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fopenmp -ffat-lto-objects -fdump-tree-gimple" } + +extern "C" void abort (); + +[[omp::directive (declare simd, linear (l))]] extern int f1 (int l); +extern int f2 (int), f3 [[omp::directive (declare simd, uniform (m))]] (int m), f4 (int), z; +[[omp::directive (declare simd, linear (l), simdlen(4))]] extern int f5 [[omp::directive (declare simd uniform (l) simdlen (8) notinbranch)]] (int l); + +int +f1 (int l) +{ + return l; +} + +// { dg-final { scan-assembler-times "_ZGVbM4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM16l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN16l__Z2f1i:" 1 { target { i?86-*-* x86_64-*-* } } } } + +int +f2 (int l) +{ + return l + 1; +} + +// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f2i:" { target { i?86-*-* x86_64-*-* } } } } + +int +f3 (int l) +{ + return l + 2; +} + +// { dg-final { scan-assembler-times "_ZGVbM4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM16u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN16u__Z2f3i:" 1 { target { i?86-*-* x86_64-*-* } } } } + +int +f4 (int l) +{ + return l + 3; +} + +// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f4i:" { target { i?86-*-* x86_64-*-* } } } } + +int +f5 (int l) +{ // { dg-warning "GCC does not currently support mixed size types for 'simd' functions" "" { target aarch64*-*-* } .-1 } + return l + 4; +} + +// { dg-final { scan-assembler-times "_ZGVbM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN4l__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN8u__Z2f5i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-not "_ZGV\[bcde]M8u__Z2f5i:" { target { i?86-*-* x86_64-*-* } } } } + +[[omp::directive (declare simd, linear (l), simdlen(4), notinbranch), + omp::directive (declare simd, uniform (l), simdlen(4), inbranch)]] +int +f6 [[omp::sequence (directive (declare simd uniform (l) simdlen (8), notinbranch), + omp::directive (declare simd linear (l) simdlen (8) inbranch))]] (int l) +{ // { dg-warning "GCC does not currently support mixed size types for 'simd' functions" "" { target aarch64*-*-* } .-2 } + return l + 5; +} + +// { dg-final { scan-assembler-times "_ZGVbM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM4u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN4l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM8l__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN8u__Z2f6i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-not "_ZGV\[bcde]M4l__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-not "_ZGV\[bcde]N4u__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-not "_ZGV\[bcde]M8u__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-not "_ZGV\[bcde]N8l__Z2f6i:" { target { i?86-*-* x86_64-*-* } } } } + +int +f7 (int l) +{ + return l + 6; +} + +// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f7i:" { target { i?86-*-* x86_64-*-* } } } } + +int +f8 (int l) +{ + return l + 7; +} + +// { dg-final { scan-assembler-not "_ZGV\[a-zA-Z0-9]__Z2f8i:" { target { i?86-*-* x86_64-*-* } } } } + +[[omp::sequence (omp::directive (declare variant (f7), match (construct={parallel})), + directive (declare simd uniform (l), simdlen(4)))]] +int +f9 [[omp::directive (declare simd uniform (l) simdlen (8)), + omp::directive (declare variant (f8) match (construct={parallel,for}))]] (int l) +{ // { dg-warning "GCC does not currently support mixed size types for 'simd' functions" "" { target aarch64*-*-* } .-2 } + return l + 8; +} + +// { dg-final { scan-assembler-times "_ZGVbM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN4u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVbN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN8u__Z2f9i:" 1 { target { i?86-*-* x86_64-*-* } } } } + +int z; + +void +test () +{ + [[omp::directive (parallel)]] + if (f9 (3) != 9) + abort (); + [[omp::directive (parallel for)]] + for (int i = 0; i < 1; i++) + if (f9 (4) != 11) + abort (); + if (f9 (5) != 13) + abort (); +} + +// { dg-final { scan-tree-dump-times " = f7 \\\(3\\\);" 1 "gimple" } } +// { dg-final { scan-tree-dump-times " = f8 \\\(4\\\);" 1 "gimple" } } +// { dg-final { scan-tree-dump-times " = f9 \\\(5\\\);" 1 "gimple" } } + +template <int N> +int +f10 (int x) +{ + return x + N; +} + +template [[omp::directive (declare simd, notinbranch)]] int f10<0> (int); + +// { dg-final { scan-assembler-times "_ZGVbN4v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN16v__Z3f10ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } + +template int f10<1> [[omp::directive (declare simd inbranch linear(x))]] (int x); + +// { dg-final { scan-assembler-times "_ZGVbM4l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM16l__Z3f10ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } + +template <int N> +int f11 (int); + +template <> [[omp::directive (declare simd, inbranch)]] int +f11<0> (int x) +{ + return x; +} + +// { dg-final { scan-assembler-times "_ZGVbM4v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM16v__Z3f11ILi0EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } + +template <> int +f11<1> [[omp::directive (declare simd, notinbranch, linear (y))]] (int y) +{ + return y; +} + +// { dg-final { scan-assembler-times "_ZGVbN4l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN16l__Z3f11ILi1EEii:" 1 { target { i?86-*-* x86_64-*-* } } } } + +struct S +{ + [[omp::sequence (directive (declare simd, inbranch, uniform (this)))]] int f12 (int x); + int f13 [[gnu::noinline, omp::directive (declare simd notinbranch uniform (this) linear (y))]] (int y) { return y; } +}; + +int +S::f12 (int x) +{ + return x; +} + +// { dg-final { scan-assembler-times "_ZGVbM4uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcM4uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdM8uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeM16uv__ZN1S3f12Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } + +// { dg-final { scan-assembler-times "_ZGVbN4ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVcN4ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVdN8ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } +// { dg-final { scan-assembler-times "_ZGVeN16ul__ZN1S3f13Ei:" 1 { target { i?86-*-* x86_64-*-* } } } } + +int +f14 (S &p, int x) +{ + return p.f13 (x); +} --- gcc/testsuite/g++.dg/gomp/attrs-11.C.jj 2021-08-09 17:41:19.316739285 +0200 +++ gcc/testsuite/g++.dg/gomp/attrs-11.C 2021-08-09 20:51:13.377428535 +0200 @@ -0,0 +1,74 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fopenmp -Wno-attributes" } + +namespace N {} +namespace O { typedef int T; }; + +void +foo () +{ + [[omp::directive (parallel)]] asm (""); // { dg-error "expected" } + [[omp::directive (parallel)]] __extension__ asm (""); // { dg-error "expected" } + __extension__ [[omp::directive (parallel)]] asm (""); // { dg-error "expected" } + [[omp::directive (parallel)]] namespace M = ::N; // { dg-error "expected" } + [[omp::directive (parallel)]] using namespace N; // { dg-bogus "expected" "" { xfail *-*-* } } + [[omp::directive (parallel)]] using O::T; // { dg-error "expected" } + [[omp::directive (parallel)]] __label__ foo; // { dg-error "expected" } + [[omp::directive (parallel)]] static_assert (true, ""); // { dg-error "expected" } + [[omp::directive (parallel)]] int a = 5; // { dg-error "not allowed to be specified in this context" } + int b = 0; + [[omp::directive (parallel)]] l: b++; // { dg-error "not allowed to be specified in this context" } + switch (0) + { + [[omp::directive (parallel)]] case 6: break; // { dg-error "not allowed to be specified in this context" } + [[omp::directive (parallel)]] default: break; // { dg-error "not allowed to be specified in this context" } + } +} + +void +bar () +{ + [[omp::directive (declare simd)]] int a; // { dg-error "not allowed to be specified in this context|not immediately followed by function declaration or definition" } + [[omp::directive (declare simd)]] int b, f1 (int); // { dg-error "not allowed to be specified in this context|not immediately followed by function declaration or definition" } + [[omp::directive (declare simd)]] int f2 (int), c; // { dg-error "not immediately followed by function declaration or definition" } + int d [[omp::directive (declare simd)]]; // { dg-error "not allowed to be specified in this context" } + int f3 [[omp::directive (declare simd)]] (int), f4 [[omp::directive (declare simd)]] (int); + __extension__ [[omp::directive (declare simd)]] int f5 (int); + __extension__ int f6 [[omp::directive (declare simd, notinbranch)]] (int); + #pragma omp declare simd notinbranch + [[omp::directive (declare simd inbranch)]] int f7 (int); // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same declaration" } + [[omp::directive (declare simd notinbranch)]] + #pragma omp declare simd inbranch // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" } + int f8 (int); + static int t1, t2, t3, t4; + [[omp::directive (declare simd), omp::directive (foobar)]] int f9 (int); // { dg-error "unknown OpenMP directive name" } + [[omp::directive (foobar), omp::directive (declare simd)]] int f10 (int); // { dg-error "unknown OpenMP directive name" } + [[omp::directive (threadprivate (t1)), omp::directive (declare simd)]] int f10 (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } + [[omp::directive (declare simd), omp::directive (threadprivate (t2))]] int f11 (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } + int f12 [[omp::directive (declare simd), omp::directive (foobar)]] (int); // { dg-error "unknown OpenMP directive name" } + int f13 [[omp::directive (foobar), omp::directive (declare simd)]] (int); // { dg-error "unknown OpenMP directive name" } + int f14 [[omp::directive (threadprivate (t3)), omp::directive (declare simd)]] (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } + int f15 [[omp::directive (declare simd), omp::directive (threadprivate (t4))]] (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } +} + +[[omp::directive (declare simd)]] int a; // { dg-error "not allowed to be specified in this context|not immediately followed by function declaration or definition" } +[[omp::directive (declare simd)]] int b, f16 (int); // { dg-error "not allowed to be specified in this context|not immediately followed by function declaration or definition" } +[[omp::directive (declare simd)]] int f17 (int), c; // { dg-error "not immediately followed by function declaration or definition" } +int d [[omp::directive (declare simd)]]; // { dg-error "not allowed to be specified in this context" } +int f18 [[omp::directive (declare simd)]] (int), f19 [[omp::directive (declare simd)]] (int); +__extension__ [[omp::directive (declare simd)]] int f20 (int); +__extension__ int f21 [[omp::directive (declare simd, notinbranch)]] (int); +#pragma omp declare simd notinbranch +[[omp::directive (declare simd inbranch)]] int f22 (int); // { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same declaration" } +[[omp::directive (declare simd notinbranch)]] // { dg-error "'declare simd' directive not immediately followed by function declaration or definition" } +#pragma omp declare simd inbranch // { dg-error "'#pragma' is not allowed here" } +int f23 (int); +int t5, t6, t7, t8; +[[omp::directive (declare simd), omp::directive (foobar)]] int f24 (int); // { dg-error "unknown OpenMP directive name" } +[[omp::directive (foobar), omp::directive (declare simd)]] int f25 (int); // { dg-error "unknown OpenMP directive name" } +[[omp::directive (threadprivate (t5)), omp::directive (declare simd)]] int f26 (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } +[[omp::directive (declare simd), omp::directive (threadprivate (t6))]] int f27 (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } +int f28 [[omp::directive (declare simd), omp::directive (foobar)]] (int); // { dg-error "unknown OpenMP directive name" } +int f29 [[omp::directive (foobar), omp::directive (declare simd)]] (int); // { dg-error "unknown OpenMP directive name" } +int f30 [[omp::directive (threadprivate (t7)), omp::directive (declare simd)]] (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } +int f31 [[omp::directive (declare simd), omp::directive (threadprivate (t8))]] (int); // { dg-error "OpenMP directive other than 'declare simd' or 'declare variant' appertains to a declaration" } Jakub