[gcc r16-9472] c++: Improve diagnostics for nullptr_t/info [PR126343]
Jakub Jelinek via Gcc-cvs <[email protected]> Fri, 31 Jul 2026 07:44:04 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:c68a5d17726fe3b57bcea127d21bf71ff53794a3 commit r16-9472-gc68a5d17726fe3b57bcea127d21bf71ff53794a3 Author: Jakub Jelinek <[email protected]> Date: Fri Jul 31 08:37:18 2026 +0200 c++: Improve diagnostics for nullptr_t/info [PR126343] 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, if cv qualified prints qualifications after them (so e.g. 'std::nullptr_t const' or 'std::meta::info volatile'). And, when printing a reflection expression, it differentiates even between the type alias case and non-alias, so for non-aliases prints 'decltype(nullptr)' or 'decltype(^^int) const volatile' etc. 2026-07-31 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. Reviewed-by: Jason Merrill <[email protected]> (cherry picked from commit 525494c18742ef917ef046584af6dd78f713a449) Diff: --- gcc/cp/error.cc | 21 +++++++++++++++- gcc/testsuite/g++.dg/cpp0x/pr124489.C | 4 +-- gcc/testsuite/g++.dg/reflect/diag6.C | 4 +-- gcc/testsuite/g++.dg/reflect/init2.C | 2 +- gcc/testsuite/g++.dg/reflect/pr126343.C | 43 +++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index a80b6e668922..55edd51ee01e 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -876,10 +876,12 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) 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: @@ -3476,7 +3478,24 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) 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; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr124489.C b/gcc/testsuite/g++.dg/cpp0x/pr124489.C index 8421270ff268..b4f4cb89f248 100644 --- a/gcc/testsuite/g++.dg/cpp0x/pr124489.C +++ b/gcc/testsuite/g++.dg/cpp0x/pr124489.C @@ -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." } } diff --git a/gcc/testsuite/g++.dg/reflect/diag6.C b/gcc/testsuite/g++.dg/reflect/diag6.C index 61caa082551d..2eed4d0b6336 100644 --- a/gcc/testsuite/g++.dg/reflect/diag6.C +++ b/gcc/testsuite/g++.dg/reflect/diag6.C @@ -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." } } diff --git a/gcc/testsuite/g++.dg/reflect/init2.C b/gcc/testsuite/g++.dg/reflect/init2.C index 4c248fd9821f..c4f2292f21d5 100644 --- a/gcc/testsuite/g++.dg/reflect/init2.C +++ b/gcc/testsuite/g++.dg/reflect/init2.C @@ -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 diff --git a/gcc/testsuite/g++.dg/reflect/pr126343.C b/gcc/testsuite/g++.dg/reflect/pr126343.C new file mode 100644 index 000000000000..beabf8661a5d --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/pr126343.C @@ -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 }