https://gcc.gnu.org/g:b77cf8f055cce3e42f09410cbbf1e4f1e196b25d
commit r17-2405-gb77cf8f055cce3e42f09410cbbf1e4f1e196b25d Author: Jakub Jelinek <[email protected]> Date: Wed Jul 15 09:21:41 2026 +0200 c++: In attributes expect _Clang namespace rather than __clang__ [PR120635] Apparently in clang __clang__ is a predefined macro (predefined to 1). Because of that, clang doesn't have alternate spelling of its scoped attributes as [[__clang__::__something__]] but [[_Clang::__something__]] The former isn't accepted even with -U__clang__. For GNU attributes they do handle [[__gnu__::__something__]] like GCC does (if they support a particular attribute). So, I'm afraid if we want to stay compatible with this mess, we need to do the same thing. The following patch does that. It emits a warning when one attempts to use the __clang__ attribute namespace suggesting to use _Clang instead (clang++ does that too), but in __has_cpp_attribute/__has_attribute doesn't do that, just ensures that 0 is returned in that case (i.e. it is not canonicalized in that case), just canonicalizes _Clang to clang. 2026-07-15 Jakub Jelinek <[email protected]> PR c++/120635 gcc/ * doc/extend.texi: Document variant spelling of standard attributes and that alternate namespace spelling for clang namespace is _Clang rather than __clang__. For no_specializations use _Clang:: rather than __clang__::. gcc/c-family/ * c-lex.cc (c_common_has_attribute): For __clang__ namespace don't call canonicalize_attr_name and for _Clang use clang instead. gcc/cp/ * parser.cc (cp_parser_std_attribute): Add canonicalize_attr_ns_name lambda and use it instead of canonicalize_attr_name for namespaces. Emit a warning on __clang__::. (cp_parser_std_attribute_spec): Emit a warning on using __clang__:. gcc/testsuite/ * g++.dg/cpp0x/attr-trivial_abi8.C: Add further tests. * g++.dg/cpp0x/attr-trivial_abi11.C: New test. * g++.dg/ext/attr-no_specializations1.C: Use _Clang:: instead of __clang__. * g++.dg/ext/attr-no_specializations2.C: Likewise. * g++.dg/ext/attr-no_specializations6.C: Likewise. * g++.dg/ext/attr-no_specializations7.C: Likewise. * g++.dg/ext/attr-no_specializations8.C: Likewise. * g++.dg/ext/attr-no_specializations10.C: Add further tests. * g++.dg/ext/attr-no_specializations12.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/c-family/c-lex.cc | 9 +++++- gcc/cp/parser.cc | 33 ++++++++++++++++------ gcc/doc/extend.texi | 8 +++++- gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi11.C | 11 ++++++++ gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi8.C | 25 ++++++++++++++++ .../g++.dg/ext/attr-no_specializations1.C | 2 +- .../g++.dg/ext/attr-no_specializations10.C | 25 ++++++++++++++++ .../g++.dg/ext/attr-no_specializations12.C | 15 ++++++++++ .../g++.dg/ext/attr-no_specializations2.C | 2 +- .../g++.dg/ext/attr-no_specializations6.C | 4 +-- .../g++.dg/ext/attr-no_specializations7.C | 4 +-- .../g++.dg/ext/attr-no_specializations8.C | 2 +- 12 files changed, 122 insertions(+), 18 deletions(-) diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc index 53c215ca9444..c987618c24e7 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -355,7 +355,6 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax) { attr_name = get_identifier ((const char *) cpp_token_as_text (pfile, token)); - attr_name = canonicalize_attr_name (attr_name); bool have_scope = false; int idx = 0; const cpp_token *nxt_token; @@ -390,6 +389,13 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax) if (nxt_token->type == CPP_NAME) { tree attr_ns = attr_name; + /* In clang, __clang__ is predefined macro, and the supported + alternate namespace is _Clang rather than __clang__ because + of that. Don't handle ___Clang__ that way though. */ + if (id_equal (attr_ns, "_Clang")) + attr_ns = get_identifier ("clang"); + else if (!id_equal (attr_ns, "__clang__")) + attr_ns = canonicalize_attr_name (attr_ns); tree attr_id = get_identifier ((const char *) cpp_token_as_text (pfile, nxt_token)); @@ -424,6 +430,7 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax) } else { + attr_name = canonicalize_attr_name (attr_name); /* Some standard attributes need special handling. */ if (c_dialect_cxx ()) { diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index d53f74126ee1..95e7544fff30 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -33298,6 +33298,7 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns) /* First, parse name of the attribute, a.k.a attribute-token. */ token = cp_lexer_peek_token (parser->lexer); + location_t id_loc = token->location; if (token->type == CPP_NAME) attr_id = token->u.value; else if (token->type == CPP_KEYWORD) @@ -33311,6 +33312,17 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns) cp_lexer_consume_token (parser->lexer); token = cp_lexer_peek_token (parser->lexer); + auto canonicalize_attr_ns_name = [] (tree attr_ns) + { + /* In clang, __clang__ is predefined macro, and the supported alternate + namespace is _Clang rather than __clang__ because of that. + Don't handle ___Clang__ that way though. */ + if (id_equal (attr_ns, "_Clang")) + attr_ns = get_identifier ("clang"); + else + attr_ns = canonicalize_attr_name (attr_ns); + return attr_ns; + }; if (token->type == CPP_SCOPE) { /* We are seeing a scoped attribute token. */ @@ -33321,6 +33333,11 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns) "with scoped attribute token"); attr_ns = attr_id; + if (id_equal (attr_ns, "__clang__")) + warning_at (id_loc, OPT_Wattributes, + "alternate attribute namespace for %<clang%> is " + "%<_Clang%> rather than %<__clang__%>"); + token = cp_lexer_peek_token (parser->lexer); if (token->type == CPP_NAME) attr_id = token->u.value; @@ -33336,7 +33353,7 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns) } cp_lexer_consume_token (parser->lexer); - attr_ns = canonicalize_attr_name (attr_ns); + attr_ns = canonicalize_attr_ns_name (attr_ns); attr_id = canonicalize_attr_name (attr_id); attribute = build_tree_list (build_tree_list (attr_ns, attr_id), NULL_TREE); @@ -33344,7 +33361,7 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns) } else if (attr_ns) { - attr_ns = canonicalize_attr_name (attr_ns); + attr_ns = canonicalize_attr_ns_name (attr_ns); attr_id = canonicalize_attr_name (attr_id); attribute = build_tree_list (build_tree_list (attr_ns, attr_id), NULL_TREE); @@ -34097,7 +34114,6 @@ cp_parser_std_attribute_spec (cp_parser *parser) && cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_SQUARE) { tree attr_ns = NULL_TREE; - tree attr_name = NULL_TREE; cp_lexer_consume_token (parser->lexer); cp_lexer_consume_token (parser->lexer); @@ -34117,12 +34133,6 @@ cp_parser_std_attribute_spec (cp_parser *parser) return attributes; } - if (token->type == CPP_NAME) - { - attr_name = token->u.value; - attr_name = canonicalize_attr_name (attr_name); - } - if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) { token = cp_lexer_peek_nth_token (parser->lexer, 2); @@ -34141,6 +34151,11 @@ cp_parser_std_attribute_spec (cp_parser *parser) "attribute using prefix only available " "with %<-std=c++17%> or %<-std=gnu++17%>"); + if (id_equal (attr_ns, "__clang__")) + warning_at (token->location, OPT_Wattributes, + "alternate attribute namespace for %<clang%> is " + "%<_Clang%> rather than %<__clang__%>"); + cp_lexer_consume_token (parser->lexer); cp_lexer_consume_token (parser->lexer); cp_lexer_consume_token (parser->lexer); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 60fc5add3bcd..4761b07973a7 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1824,6 +1824,12 @@ and suffixed by two underscores; for example, the alternate name of @code{weak} is @code{__weak__}. These alternate names are useful in library header files in case they are included into files that have redefined the base name of the attribute as a preprocessor macro. +Similarly for every supported standard or scoped attribute, for example, the +variant spellings of @code{[[gnu::weak]]} are @code{[[__gnu__::__weak__]]}, +@code{[[gnu::__weak__]]} or @code{[[__gnu__::weak]]}. The only exception +is that alternate spelling of the @code{clang} scoped attribute namespace +is @code{_Clang} rather than @code{__clang__} for compatibility with that +compiler. GCC also accepts the keyword @code{__attribute} as a synonym for @code{__attribute__}. @@ -31689,7 +31695,7 @@ struct __attribute__ ((__no_specializations__)) A @{@}; template <typename T, typename U> [[clang::no_specializations]] int b = 1; template <typename T> -[[__clang__::__no_specializations__ ("foo cannot be specialized")]] int +[[_Clang::__no_specializations__ ("foo cannot be specialized")]] int foo () @{ return 42; @} template <> struct A <int> @{@}; // Error template <typename T> int b <T, T> = 2; // Error diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi11.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi11.C new file mode 100644 index 000000000000..3940a4bc3fd7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi11.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++11 } } +// Test basic functionality of [[clang::trivial_abi]] attribute + +struct [[_Clang::__trivial_abi__]] A { int a; ~A () {} }; +#if __cplusplus >= 201703L +struct [[using _Clang:__trivial_abi__]] B { int b; ~B () {} }; +#endif +struct [[__clang__::__trivial_abi__]] C { int c; ~C () {} }; // { dg-warning "alternate attribute namespace for 'clang' is '_Clang' rather than '__clang__'" } +#if __cplusplus >= 201703L +struct [[using __clang__:__trivial_abi__]] D { int d; ~D () {} }; // { dg-warning "alternate attribute namespace for 'clang' is '_Clang' rather than '__clang__'" "" { target c++17 } } +#endif diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi8.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi8.C index 48331ab5adaf..c11ecdf96830 100644 --- a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi8.C +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi8.C @@ -19,3 +19,28 @@ #if __has_attribute (trivial_abi) != 1 #error #endif +#if __has_cpp_attribute (_Clang::__trivial_abi__) != 1 +#error +#endif +#if __has_cpp_attribute (__gnu__::__trivial_abi__) != 0 +#error +#endif +#if __has_cpp_attribute (__trivial_abi__) != 0 +#error This fails for now +// { dg-bogus "This fails for now" "" { xfail *-*-* } .-1 } +#endif +#if __has_attribute (_Clang::__trivial_abi__) != 1 +#error +#endif +#if __has_attribute (__gnu__::__trivial_abi__) != 0 +#error +#endif +#if __has_attribute (__trivial_abi__) != 1 +#error +#endif +#if __has_cpp_attribute (__clang__::__trivial_abi__) != 0 +#error +#endif +#if __has_attribute (__clang__::__trivial_abi__) != 0 +#error +#endif diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C index 69dee86d3826..4c815edc3a15 100644 --- a/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations1.C @@ -8,7 +8,7 @@ template <typename T, typename U> [[clang::no_specializations]] int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } #endif template <typename T, typename U> -[[__clang__::__no_specializations__]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +[[_Clang::__no_specializations__]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } template <typename T> struct B { struct [[clang::no_specializations]] C {}; // { dg-warning "'no_specializations' attribute only applies to class, function or variable templates" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C index fc3de8969f10..8e718b15841d 100644 --- a/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations10.C @@ -20,3 +20,28 @@ #if __has_attribute (no_specializations) != 1 #error #endif +#if __has_cpp_attribute (_Clang::__no_specializations__) != 1 +#error +#endif +#if __has_cpp_attribute (__gnu__::__no_specializations__) != 0 +#error +#endif +#if __has_cpp_attribute (__no_specializations__) != 0 +#error This fails for now +// { dg-bogus "This fails for now" "" { xfail *-*-* } .-1 } +#endif +#if __has_attribute (_Clang::__no_specializations__) != 1 +#error +#endif +#if __has_attribute (__gnu__::__no_specializations__) != 0 +#error +#endif +#if __has_attribute (__no_specializations__) != 1 +#error +#endif +#if __has_cpp_attribute (__clang__::__no_specializations__) != 0 +#error +#endif +#if __has_attribute (__clang__::__no_specializations__) != 0 +#error +#endif diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations12.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations12.C new file mode 100644 index 000000000000..54cae1e9c310 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations12.C @@ -0,0 +1,15 @@ +// PR c++/120635 +// { dg-do compile { target c++17 } } + +template <typename T, typename U> +struct [[using _Clang:__no_specializations__]] A {}; +template <typename T, typename U> +struct [[using _Clang:__no_specializations__ ("foo")]] B {}; +template <typename T, typename U> +struct [[__clang__::__no_specializations__]] C {}; // { dg-warning "alternate attribute namespace for 'clang' is '_Clang' rather than '__clang__'" } +template <typename T, typename U> +struct [[__clang__::__no_specializations__ ("foo")]] D {}; // { dg-warning "alternate attribute namespace for 'clang' is '_Clang' rather than '__clang__'" } +template <typename T, typename U> +struct [[using __clang__:__no_specializations__]] E {}; // { dg-warning "alternate attribute namespace for 'clang' is '_Clang' rather than '__clang__'" } +template <typename T, typename U> +struct [[using __clang__:__no_specializations__ ("foo")]] F {}; // { dg-warning "alternate attribute namespace for 'clang' is '_Clang' rather than '__clang__'" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C index 9ebee5e94e3f..ae8a71e9ee6f 100644 --- a/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations2.C @@ -5,7 +5,7 @@ template <typename T, typename U> union [[clang::no_specializations ("reason1")]] A {}; // { dg-message "declared 'clang::no_specializations' here" } #if __cpp_variable_templates >= 201304 template <typename T, typename U> -[[__clang__::__no_specializations__ ("reason2")]] int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } +[[_Clang::__no_specializations__ ("reason2")]] int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } #endif template <typename T, typename U> [[clang::no_specializations ("reason3")]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C index 81e435a3455e..3e2fabd32738 100644 --- a/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations6.C @@ -9,7 +9,7 @@ template <typename T, typename U> [[clang::no_specializations]] constexpr int b = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } #endif template <typename T, typename U> -[[__clang__::__no_specializations__]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +[[_Clang::__no_specializations__]] int foo () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } template <typename T> struct D {}; template <> @@ -35,7 +35,7 @@ template <typename T, typename U> [[clang::no_specializations ("my other reason")]] int c = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } #endif template <typename T, typename U> -[[__clang__::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +[[_Clang::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } template <> struct B <int, int> { int a; }; // { dg-error "'struct B<int, int>' cannot be specialized: 'this is why'" } template <typename T> diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C index be88e76e587f..492740f52cfc 100644 --- a/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations7.C @@ -9,7 +9,7 @@ template <typename T, typename U> [[clang::no_specializations]] constexpr int b = 1; // { dg-bogus "declared 'clang::no_specializations' here" } #endif template <typename T, typename U> -[[__clang__::__no_specializations__]] int foo () { return 0; } // { dg-bogus "declared 'clang::no_specializations' here" } +[[_Clang::__no_specializations__]] int foo () { return 0; } // { dg-bogus "declared 'clang::no_specializations' here" } template <typename T> struct D {}; template <> @@ -35,7 +35,7 @@ template <typename T, typename U> [[clang::no_specializations ("my other reason")]] int c = 1; // { dg-bogus "declared 'clang::no_specializations' here" } #endif template <typename T, typename U> -[[__clang__::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-bogus "declared 'clang::no_specializations' here" } +[[_Clang::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-bogus "declared 'clang::no_specializations' here" } template <> struct B <int, int> { int a; }; // { dg-bogus "'struct B<int, int>' cannot be specialized: 'this is why'" } template <typename T> diff --git a/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C b/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C index 7cef40c3dffc..3bf4e2a66d63 100644 --- a/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C +++ b/gcc/testsuite/g++.dg/ext/attr-no_specializations8.C @@ -10,7 +10,7 @@ template <typename T, typename U> [[clang::no_specializations ("my other reason")]] int c = 1; // { dg-message "declared 'clang::no_specializations' here" "" { target c++14 } } #endif template <typename T, typename U> -[[__clang__::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } +[[_Clang::__no_specializations__ ("bar cannot be specialized")]] int bar () { return 0; } // { dg-message "declared 'clang::no_specializations' here" } template <typename T> struct D {}; template <>
