[gcc r17-2405] c++: In attributes expect _Clang namespace rather than __clang__ [PR120635]

Jakub Jelinek via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
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 <>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.