Hi!

We currently print std::nullptr_t or std::meta::info in diagnostics
when seeing a NULLPTR_TYPE or META_TYPE, when they aren't type aliases
(or when they are exactly those type aliases).
I think that isn't a bad idea, the aliases is what users usually
use for those.  There are 2 problems with this though.
We print
decltype(nullptr)
and
decltype(nullptr) const volatile
exactly the same, both as std::nullptr_t, so the qualifiers are lost.
And, e.g. in case of a static assertion failure when people want to find
out why some reflections aren't equal we can print
note: the comparison reduces to '(^^std::meta::info == ^^std::meta:info)'
and the user then has no idea what is going on.  Is it because one of
those is a type alias (which one), or because of cv-qual differences,
or both?
The following patch prints the aliases in normal %qT etc. printing
only if unqualified, when qualified prints decltype(^^int) or
decltype(nullptr) with the qualifications.
And, when printing a reflection expression, it differentiates even
between the type alias case and non-alias.

So far lightly tested, ok for trunk if it passes full bootstrap/regtest?

Or do you prefer always printing decltype?

2026-07-22  Jakub Jelinek  <[email protected]>

        PR c++/126343
        * error.cc (dump_type) <case NULLPTR_TYPE>: For qualified
        NULLPTR_TYPE print decltype(nullptr) quals.
        (dump_type) <case META_TYPE>: For qualified META_TYPE print
        decltype(^^int) quals.
        (dump_expr) <case REFLECT_EXPR>: For REFLECT_EXPR on
        non-typedef non-qualified META_TYPE or NULLPTR_TYPE print
        decltype(^^int) or decltype(nullptr).

        * g++.dg/reflect/pr126343.C: New test.
        * g++.dg/reflect/diag6.C: Adjust expected diagnostic wording.
        * g++.dg/reflect/init2.C: Likewise.

--- gcc/cp/error.cc.jj  2026-07-13 18:35:54.649463659 +0200
+++ gcc/cp/error.cc     2026-07-22 09:56:36.155416491 +0200
@@ -875,11 +875,27 @@ dump_type (cxx_pretty_printer *pp, tree
       break;
 
     case NULLPTR_TYPE:
-      pp_cxx_ws_string (pp, "std::nullptr_t");
+      /* Print std::nullptr_t if it is unqualified, otherwise e.g.
+        decltype(nullptr) const.  */
+      if (TYPE_QUALS (t))
+       {
+         pp_cxx_ws_string (pp, "decltype(nullptr)");
+         pp_c_type_qualifier_list (pp, t);
+       }
+      else
+       pp_cxx_ws_string (pp, "std::nullptr_t");
       break;
 
     case META_TYPE:
-      pp_cxx_ws_string (pp, "std::meta::info");
+      /* Print std::meta::info if it is unqualified, otherwise e.g.
+        decltype(^^int) const.  */
+      if (TYPE_QUALS (t))
+       {
+         pp_cxx_ws_string (pp, "decltype(^^int)");
+         pp_c_type_qualifier_list (pp, t);
+       }
+      else
+       pp_cxx_ws_string (pp, "std::meta::info");
       break;
 
     case SPLICE_SCOPE:
@@ -3481,7 +3497,21 @@ dump_expr (cxx_pretty_printer *pp, tree
            if (DECL_P (h))
              dump_decl (pp, h, flags);
            else if (TYPE_P (h))
-             dump_type (pp, h, flags);
+             {
+               /* For reflection we care about the difference
+                  between std::meta::info/std::nullptr_t and
+                  decltype(^^int)/decltype(nullptr).  */
+               if (TREE_CODE (h) == META_TYPE
+                   && !typedef_variant_p (h)
+                   && !TYPE_QUALS (h))
+                 pp_cxx_ws_string (pp, "decltype(^^int)");
+               else if (TREE_CODE (h) == NULLPTR_TYPE
+                        && !typedef_variant_p (h)
+                        && !TYPE_QUALS (h))
+                 pp_cxx_ws_string (pp, "decltype(nullptr)");
+               else
+                 dump_type (pp, h, flags);
+             }
            else
              dump_expr (pp, h, flags);
            break;
--- gcc/testsuite/g++.dg/reflect/pr126343.C.jj  2026-07-22 10:15:07.629513229 
+0200
+++ gcc/testsuite/g++.dg/reflect/pr126343.C     2026-07-22 10:19:29.149019109 
+0200
@@ -0,0 +1,43 @@
+// PR c++/126343
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+namespace std {
+  namespace meta {
+    using info = decltype (^^::);
+  }
+  using nullptr_t = decltype (nullptr);
+}
+namespace my {
+  using info = decltype (^^::) const;
+  using nullptr_t = volatile decltype (nullptr);
+}
+constexpr auto a = ^^decltype (^^int);
+constexpr auto b = ^^decltype (nullptr);
+constexpr auto c = ^^const decltype (^^int);
+constexpr auto d = ^^const decltype (nullptr);
+constexpr auto e = ^^decltype (^^int) const volatile;
+constexpr auto f = ^^decltype (nullptr) volatile;
+constexpr auto g = ^^std::meta::info;
+constexpr auto h = ^^std::nullptr_t;
+constexpr auto i = ^^my::info;
+constexpr auto j = ^^my::nullptr_t;
+static_assert (a == ^^char);           // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^decltype\\\(\\\^\\\^int\\\) == \\\^\\\^char\\\)'" "" { 
target *-*-* } .-1 }
+static_assert (b == ^^short);          // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^decltype\\\(nullptr\\\) == \\\^\\\^short int\\\)'" "" { 
target *-*-* } .-1 }
+static_assert (c == ^^int);            // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^decltype\\\(\\\^\\\^int\\\) const == \\\^\\\^int\\\)'" 
"" { target *-*-* } .-1 }
+static_assert (d == ^^long);           // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^decltype\\\(nullptr\\\) const == \\\^\\\^long int\\\)'" 
"" { target *-*-* } .-1 }
+static_assert (e == ^^long long);      // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^decltype\\\(\\\^\\\^int\\\) const volatile == 
\\\^\\\^long long int\\\)'" "" { target *-*-* } .-1 }
+static_assert (f == ^^unsigned char);  // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^decltype\\\(nullptr\\\) volatile == \\\^\\\^unsigned 
char\\\)'" "" { target *-*-* } .-1 }
+static_assert (g == ^^unsigned short); // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^std::meta::info == \\\^\\\^short unsigned int\\\)'" "" 
{ target *-*-* } .-1 }
+static_assert (h == ^^unsigned int);   // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^std::nullptr_t == \\\^\\\^unsigned int\\\)'" "" { 
target *-*-* } .-1 }
+static_assert (i == ^^unsigned long);  // { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^my::info == \\\^\\\^long unsigned int\\\)'" "" { target 
*-*-* } .-1 }
+static_assert (j == ^^unsigned long long);// { dg-error "static assertion " }
+                                       // { dg-message "note: the comparison 
reduces to '\\\(\\\^\\\^my::nullptr_t == \\\^\\\^long long unsigned int\\\)'" 
"" { target *-*-* } .-1 }
--- gcc/testsuite/g++.dg/reflect/diag6.C.jj     2026-03-27 10:17:16.121298315 
+0100
+++ gcc/testsuite/g++.dg/reflect/diag6.C        2026-07-22 10:21:14.401616096 
+0200
@@ -5,6 +5,6 @@
 void
 g ()
 {
-  constexpr decltype(^^::) dm = ^^int; // { dg-message ".constexpr 
std::meta::info dm. previously declared here" }
-  constexpr decltype(^^::) dm = ^^int; // { dg-error "redeclaration of 
.constexpr std::meta::info dm." }
+  constexpr decltype(^^::) dm = ^^int; // { dg-message ".constexpr 
decltype\\\(\\\^\\\^int\\\) const dm. previously declared here" }
+  constexpr decltype(^^::) dm = ^^int; // { dg-error "redeclaration of 
.constexpr decltype\\\(\\\^\\\^int\\\) const dm." }
 }
--- gcc/testsuite/g++.dg/reflect/init2.C.jj     2026-03-27 10:17:16.127298217 
+0100
+++ gcc/testsuite/g++.dg/reflect/init2.C        2026-07-22 10:25:46.159993563 
+0200
@@ -8,7 +8,7 @@ f ()
   constexpr static auto srefl = ^^int;
   constexpr auto *p = &srefl;
   constexpr auto **q = &p;  // { dg-error "unable to deduce" }
-  // { dg-message "types .auto\\*. and .std::meta::info\\* const." "" { target 
*-*-* } .-1 }
+  // { dg-message "types .auto\\*. and .decltype\\\(\\\^\\\^int\\\) const\\* 
const." "" { target *-*-* } .-1 }
 }
 
 void

        Jakub

Reply via email to