Bug 54043 - [LWG 2221] cout << nullptr does not work
Summary: [LWG 2221] cout << nullptr does not work
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.6.3
: P3 enhancement
Target Milestone: 9.0
Assignee: Ville Voutilainen
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-07-20 08:46 UTC by Aryeh Gregor
Modified: 2020-01-09 14:09 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2012-07-20 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Aryeh Gregor 2012-07-20 08:46:15 UTC
I was converting the Mozilla codebase to use nullptr instead of 0L and fixing the resulting compile errors <https://bugzilla.mozilla.org/show_bug.cgi?id=626472> when I ran into this.  Test program:

<<<<
#include <iostream>

int main()
{
        std::cout << nullptr << std::endl;
}
>>>>

Command line and output:

<<<<
$ g++ -v -Wall -Wextra -Werror -std=c++0x test.cpp -otest
Using built-in specs.
COLLECT_GCC=/usr/bin/g++-4.6.real
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.6/lto-wrapper
Target: i686-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
Thread model: posix
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
COLLECT_GCC_OPTIONS='-v' '-Wall' '-Wextra' '-Werror' '-std=c++0x' '-o' 'test' '-shared-libgcc' '-mtune=generic' '-march=i686'
 /usr/lib/gcc/i686-linux-gnu/4.6/cc1plus -quiet -v -imultilib . -imultiarch i386-linux-gnu -D_GNU_SOURCE test.cpp -quiet -dumpbase test.cpp -mtune=generic -march=i686 -auxbase test -Wall -Wextra -Werror -std=c++0x -version -fstack-protector -o /tmp/cca9VYQZ.s
GNU C++ (Ubuntu/Linaro 4.6.3-1ubuntu5) version 4.6.3 (i686-linux-gnu)
        compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/i386-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i686-linux-gnu/4.6/../../../../i686-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/4.6
 /usr/include/c++/4.6/i686-linux-gnu/.
 /usr/include/c++/4.6/backward
 /usr/lib/gcc/i686-linux-gnu/4.6/include
 /usr/local/include
 /usr/lib/gcc/i686-linux-gnu/4.6/include-fixed
 /usr/include/i386-linux-gnu
 /usr/include
End of search list.
GNU C++ (Ubuntu/Linaro 4.6.3-1ubuntu5) version 4.6.3 (i686-linux-gnu)
        compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 2ed62271b86e2b75137544459bab1a81
test.cpp: In function ‘int main()’:
test.cpp:5:15: error: ambiguous overload for ‘operator<<’ in ‘std::cout << nullptr’
test.cpp:5:15: note: candidates are:
/usr/include/c++/4.6/ostream:110:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char, _Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
/usr/include/c++/4.6/ostream:119:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ios_type& (*)(std::basic_ostream<_CharT, _Traits>::__ios_type&)) [with _CharT = char, _Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>, std::basic_ostream<_CharT, _Traits>::__ios_type = std::basic_ios<char>]
/usr/include/c++/4.6/ostream:129:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
/usr/include/c++/4.6/ostream:175:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
/usr/include/c++/4.6/ostream:227:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
/usr/include/c++/4.6/bits/ostream.tcc:121:5: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__streambuf_type*) [with _CharT = char, _Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__streambuf_type = std::basic_streambuf<char>]
/usr/include/c++/4.6/ostream:581:5: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::nullptr_t] <near match>
/usr/include/c++/4.6/ostream:581:5: note:   no known conversion for argument 1 from ‘std::ostream {aka std::basic_ostream<char>}’ to ‘std::basic_ostream<char>&&’
/usr/include/c++/4.6/ostream:528:5: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const unsigned char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.6/ostream:523:5: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const signed char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.6/ostream:510:5: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.6/bits/ostream.tcc:323:5: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*) [with _CharT = char, _Traits = std::char_traits<char>]
>>>>


In case you're wondering why anyone would bother sending a nullptr_t to a stream, given that it only has one possible value: this came up in Mozilla's codebase in a macro:

"do_check_eq(valueArray->AsSharedBlob(0, &len), nsnull);"
http://hg.mozilla.org/mozilla-central/file/3a05d298599e/storage/test/test_AsXXX_helpers.cpp#l52

nsnull was formerly a macro for 0L (or 0LL on Win64), but I was in the process of changing it to nullptr (see aforementioned bug, https://bugzilla.mozilla.org/show_bug.cgi?id=626472) to get better type-safety.  do_check_eq is a macro that checks its arguments' equality, and if they fail, prints an error:

http://hg.mozilla.org/mozilla-central/file/3a05d298599e/storage/test/storage_test_harness.h#l57

It uses std::ostringstream to hold the error message, and relies on << to print the expected and actual values according to their respective types.  The variable aActual in this case happens to be nsnull, so it fails due to this error.  In the absence of macros, obviously, it's not likely anyone would try outputting a nullptr to a stream, but the use-case seems legitimate to me.


Reading <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3376.pdf>, I find in 27.7.3.1 (p. 1010) that basic_ostream<charT,traits>& operator<< is defined with a number of operators that nullptr_t will convert to, including void* and bool.  13.3.3.1.1 seems to say (p. 288) that any pointer conversion or boolean conversion has rank "conversion".  4.10 (p. 80) says that nullptr_t can be converted to any pointer type as a pointer conversion, and 4.12 (p. 81) says it can be converted to bool as a boolean conversion.  Thus, per spec, gcc appears to be correct -- the call is ambiguous, since there are multiple equally good viable functions.  (I might be wrong, since I didn't read the spec thoroughly.  My background is more web standards.)

However, this does not seem at all expected behavior.  I would expect printing nullptr to a stream to work, probably printing the same as (void*)0.  I don't know what gcc's policy is on deviating from the spec in cases like this -- please advise.  Perhaps someone on the gcc team who works with WG21 could refer this to them for discussion, to change at the next opportunity -- either in the next major version, or perhaps in errata (I don't know what WG21's policy is on minor updates).


If you need any more information, please let me know.  Thanks.
Comment 1 Jonathan Wakely 2012-07-20 10:21:13 UTC
I think this behaviour is correct, do you have a standard reference indicating otherwise?
Comment 2 Aryeh Gregor 2012-07-20 12:00:57 UTC
See third-to-last paragraph of comment #0.  AFAICT, gcc is currently correct according to the standard, but I think the behavior specified by the standard is undesirable.  If gcc maintainers agree that the current behavior is correct per standard but also that it's undesirable, I would like to talk about how this could be brought up with WG21 to be fixed in the standard.  If you think I should speak to someone else about fixing this in the standard, please advise me who I should talk to.  Thanks.
Comment 3 Jonathan Wakely 2012-07-20 12:05:01 UTC
You can submit an issue, see http://cplusplus.github.com/LWG/lwg-active.html#submit_issue
Comment 4 Jonathan Wakely 2012-07-20 12:17:48 UTC
In fact it was discussed last August, one of the final comments was "It also feels like it's not a defect, so it would have to wait for a new TR or standard."
Comment 5 Aryeh Gregor 2012-07-20 12:39:00 UTC
Thanks.  Is there any publicly-accessible summary of the previous discussion, so that I can read it and not retread old ground?  Also, if the WG agrees to make the change in the next version, would gcc be willing to implement it right away rather than waiting for the next version of the standard to actually be released?  (I'm assuming yes, since there was a bunch of C++0x stuff implemented before it was actually released.)
Comment 6 Jonathan Wakely 2012-07-20 12:46:36 UTC
(In reply to comment #5)
> Thanks.  Is there any publicly-accessible summary of the previous discussion,

No, although there wasn't much discussion really. Someone asked if it's supposed to work or not, the conclusion was it's not meant to.  It was pointed out that it can happen easily with e.g.

BOOST_CHECK_EQUAL( ptr, nullptr );

> so that I can read it and not retread old ground?  Also, if the WG agrees to
> make the change in the next version, would gcc be willing to implement it right
> away rather than waiting for the next version of the standard to actually be
> released?  (I'm assuming yes, since there was a bunch of C++0x stuff
> implemented before it was actually released.)

Yes, I imagine so.  It could change the meaning of valid code but probably wouldn't cause problems.  I suppose we could do it now and put it in a separate header such as <ext/nullptr_io.h> so it isn't an overload candidate unless that's included. That obviously wouldn't be portable.
Comment 7 Daniel Krügler 2012-07-20 13:59:41 UTC
In this context it is presumably interesting to mention a recently intended core language change:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1423

I believe this change will have effects on this issue: Once accepted, the nullptr_t -> bool conversion will no longer be considered (I have not yet checked whether there would still exist other conversion ambiguities).

Jonathan, given the state of 1423, should I open a new reminder bugzilla entry to take care of this? AFAIK we have done so for other "ready" issues in the past.
Comment 8 Daniel Krügler 2012-07-20 14:04:43 UTC
(In reply to comment #7)
> In this context it is presumably interesting to mention a recently intended
> core language change:
> 
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1423
> 
> I believe this change will have effects on this issue: Once accepted, the
> nullptr_t -> bool conversion will no longer be considered (I have not yet
> checked whether there would still exist other conversion ambiguities).

I just recognize that this won't change the ambiguity, because we have still enough left ;-)
 
> Jonathan, given the state of 1423, should I open a new reminder bugzilla entry
> to take care of this? AFAIK we have done so for other "ready" issues in the
> past.

I still think that adding this entry will be good to have. Agreed?
Comment 9 Jonathan Wakely 2012-07-20 14:14:36 UTC
(In reply to comment #8)
> > Jonathan, given the state of 1423, should I open a new reminder bugzilla entry
> > to take care of this? AFAIK we have done so for other "ready" issues in the
> > past.
> 
> I still think that adding this entry will be good to have. Agreed?

Sure, no harm in adding it - if Jason doesn't want to implement it yet then he won't :)

Thanks for pointing out the issue.
Comment 10 Daniel Krügler 2012-07-20 19:02:35 UTC
(In reply to comment #9)
> (In reply to comment #8)
> > > Jonathan, given the state of 1423, should I open a new reminder bugzilla entry
> > > to take care of this? AFAIK we have done so for other "ready" issues in the
> > > past.
> > 
> > I still think that adding this entry will be good to have. Agreed?
> 
> Sure, no harm in adding it - if Jason doesn't want to implement it yet then he
> won't :)
> 
> Thanks for pointing out the issue.

I just found out that you already added it a while ago via bug 52174. I added some further comments in regard to the ready state and suggested some test cases.
Comment 11 Aryeh Gregor 2012-07-23 11:00:01 UTC
(In reply to comment #3)
> You can submit an issue, see
> http://cplusplus.github.com/LWG/lwg-active.html#submit_issue

I sent an e-mail to Alisdair Meredith per the instructions on that page.
Comment 12 Paolo Carlini 2013-03-04 10:44:24 UTC
Then I suppose that if anything this is library, not core, even if there are interactions. Is there an open LWG DR?
Comment 13 Daniel Krügler 2013-03-04 10:46:27 UTC
(In reply to comment #12)
> Then I suppose that if anything this is library, not core, even if there are
> interactions. Is there an open LWG DR?

You may want to refer to:

http://cplusplus.github.com/LWG/lwg-active.html#2221
Comment 14 Paolo Carlini 2013-03-04 10:50:26 UTC
Ah great, thanks Daniel.
Comment 15 Jonathan Wakely 2017-01-20 00:21:02 UTC
The result is supposed to be a null-terminated string, so we could do what glibc's printf does for null pointers and print "(nil)" but we'd have to widen the string to the stream's char_type. Alternatively we could just print '0'.

Either way we probably need to define the function for C++11 or compile the src/c++11/ostream-inst.cc file as C++17 so that the explicit instantiation definition includes the new member function.
Comment 16 Ville Voutilainen 2017-12-05 11:06:03 UTC
Mine.
Comment 17 Ville Voutilainen 2017-12-05 11:06:57 UTC
Initial patch is at https://gcc.gnu.org/ml/gcc-patches/2017-12/msg00112.html, will need to wait until next stage1 to continue on it.
Comment 18 Jonathan Wakely 2020-01-09 14:09:14 UTC
This was fixed for GCC 9.1 by r267808