On 7/27/26 4:40 PM, Jakub Jelinek wrote:
On Fri, Jul 24, 2026 at 03:06:14PM -0400, Jason Merrill wrote:
On 7/22/26 4:44 AM, Jakub Jelinek wrote:
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?

In the non-reflection case, we could continue to always print nullptr_t and
info, just add the cv-quals?

Ok, here is an updated version which does it that way.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.
2026-07-27  Jakub Jelinek  <[email protected]>

        PR c++/126343
        * error.cc (dump_type) <case NULLPTR_TYPE>: Call
        pp_c_type_qualifier_list.
        (dump_type) <case META_TYPE>: Likewise.
        (dump_expr) <case REFLECT_EXPR>: For REFLECT_EXPR on
        non-typedef 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.
        * g++.dg/cpp0x/pr124489.C: Likewise.

--- gcc/cp/error.cc.jj  2026-07-22 11:41:35.683252926 +0200
+++ gcc/cp/error.cc     2026-07-26 15:40:27.586800726 +0200
@@ -876,10 +876,12 @@ dump_type (cxx_pretty_printer *pp, tree
case NULLPTR_TYPE:
        pp_cxx_ws_string (pp, "std::nullptr_t");
+      pp_c_type_qualifier_list (pp, t);
        break;
case META_TYPE:
        pp_cxx_ws_string (pp, "std::meta::info");
+      pp_c_type_qualifier_list (pp, t);
        break;
case SPLICE_SCOPE:
@@ -3481,7 +3483,24 @@ 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))
+                 {
+                   pp_cxx_ws_string (pp, "decltype(^^int)");
+                   pp_c_type_qualifier_list (pp, h);
+                 }
+               else if (TREE_CODE (h) == NULLPTR_TYPE
+                        && !typedef_variant_p (h))
+                 {
+                   pp_cxx_ws_string (pp, "decltype(nullptr)");
+                   pp_c_type_qualifier_list (pp, h);
+                 }
+               else
+                 dump_type (pp, h, flags);
+             }
            else
              dump_expr (pp, h, flags);
            break;
--- gcc/testsuite/g++.dg/cpp0x/pr124489.C.jj    2026-03-27 10:17:15.510308285 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/pr124489.C       2026-07-26 16:08:22.216399495 
+0200
@@ -4,6 +4,6 @@
  void
  g ()
  {
-  constexpr decltype(nullptr) dm = nullptr; // { dg-message ".constexpr 
std::nullptr_t dm. previously declared here" }
-  constexpr decltype(nullptr) dm = nullptr; // { dg-error "redeclaration of 
.constexpr std::nullptr_t dm." }
+  constexpr decltype(nullptr) dm = nullptr; // { dg-message ".constexpr 
std::nullptr_t const dm. previously declared here" }
+  constexpr decltype(nullptr) dm = nullptr; // { dg-error "redeclaration of 
.constexpr std::nullptr_t const dm." }
  }
--- gcc/testsuite/g++.dg/reflect/pr126343.C.jj  2026-07-26 15:38:12.536398557 
+0200
+++ gcc/testsuite/g++.dg/reflect/pr126343.C     2026-07-26 15:38:12.536398557 
+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-07-22 11:41:35.685252899 
+0200
+++ gcc/testsuite/g++.dg/reflect/diag6.C        2026-07-26 16:09:48.561296031 
+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 std::meta::info 
const dm. previously declared here" }
+  constexpr decltype(^^::) dm = ^^int; // { dg-error "redeclaration of .constexpr 
std::meta::info const dm." }
  }
--- gcc/testsuite/g++.dg/reflect/init2.C.jj     2026-07-22 11:41:35.685252899 
+0200
+++ gcc/testsuite/g++.dg/reflect/init2.C        2026-07-26 16:10:21.208878807 
+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 .std::meta::info const\\* const." "" { 
target *-*-* } .-1 }
  }
void


        Jakub


Reply via email to