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 <>

Reply via email to