Re: [PATCH] c++, v2: Improve diagnostics for nullptr_t/info [PR126343]

Jason Merrill <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
>
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.