[libc++] Implement LWG1203

Libc++ had an issue where nonsensical code like

  decltype(std::stringstream{} << std::vector<int>{});

would compile, as long as you kept the expression inside decltype in
an unevaluated operand. This turned out to be that we didn't implement
LWG1203, which clarifies what we should do in that case.

rdar://58769296
This commit is contained in:
Louis Dionne
2020-09-22 15:46:37 -04:00
parent 49f2744931
commit fdc41e11f9
7 changed files with 118 additions and 45 deletions

View File

@@ -150,9 +150,9 @@ template <class charT, class traits>
basic_istream<charT,traits>&
ws(basic_istream<charT,traits>& is);
template <class charT, class traits, class T>
basic_istream<charT, traits>&
operator>>(basic_istream<charT, traits>&& is, T& x);
// rvalue stream extraction
template <class Stream, class T>
Stream&& operator>>(Stream&& is, T&& x);
} // std
@@ -1378,13 +1378,23 @@ ws(basic_istream<_CharT, _Traits>& __is)
#ifndef _LIBCPP_CXX03_LANG
template <class _CharT, class _Traits, class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>&& __is, _Tp&& __x)
template <class _Stream, class _Tp, class = void>
struct __is_istreamable : false_type { };
template <class _Stream, class _Tp>
struct __is_istreamable<_Stream, _Tp, decltype(
_VSTD::declval<_Stream>() >> _VSTD::declval<_Tp>(), void()
)> : true_type { };
template <class _Stream, class _Tp, class = enable_if_t<
_And<is_base_of<ios_base, _Stream>,
__is_istreamable<_Stream&, _Tp&&>>::value
>>
_LIBCPP_INLINE_VISIBILITY
_Stream&& operator>>(_Stream&& __is, _Tp&& __x)
{
__is >> _VSTD::forward<_Tp>(__x);
return __is;
return _VSTD::move(__is);
}
#endif // _LIBCPP_CXX03_LANG

View File

@@ -126,9 +126,8 @@ template <class charT, class traits>
basic_ostream<charT,traits>& flush(basic_ostream<charT,traits>& os);
// rvalue stream insertion
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);
template <class Stream, class T>
Stream&& operator<<(Stream&& os, const T& x);
} // std
@@ -1028,15 +1027,20 @@ flush(basic_ostream<_CharT, _Traits>& __os)
#ifndef _LIBCPP_CXX03_LANG
template <class _Stream, class _Tp, class = void>
struct __is_ostreamable : false_type { };
template <class _Stream, class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
typename enable_if
<
!is_lvalue_reference<_Stream>::value &&
is_base_of<ios_base, _Stream>::value,
_Stream&&
>::type
operator<<(_Stream&& __os, const _Tp& __x)
struct __is_ostreamable<_Stream, _Tp, decltype(
_VSTD::declval<_Stream>() << _VSTD::declval<_Tp>(), void()
)> : true_type { };
template <class _Stream, class _Tp, class = enable_if_t<
_And<is_base_of<ios_base, _Stream>,
__is_ostreamable<_Stream&, const _Tp&>>::value
>>
_LIBCPP_INLINE_VISIBILITY
_Stream&& operator<<(_Stream&& __os, const _Tp& __x)
{
__os << __x;
return _VSTD::move(__os);

View File

@@ -0,0 +1,20 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
// Make sure the rvalue overload of operator>> isn't part of the overload set
// when the type is not input streamable from a lvalue stream.
#include <istream>
#include <utility>
struct Foo { };
using X = decltype(std::declval<std::istream>() >> std::declval<Foo&>()); // expected-error {{invalid operands to binary expression}}
using Y = decltype(std::declval<std::istream>() >> std::declval<Foo>()); // expected-error {{invalid operands to binary expression}}

View File

@@ -10,9 +10,8 @@
// <istream>
// template <class charT, class traits, class T>
// basic_istream<charT, traits>&
// operator>>(basic_istream<charT, traits>&& is, T&& x);
// template <class Stream, class T>
// Stream&& operator>>(Stream&& is, T&& x);
#include <istream>
#include <sstream>
@@ -44,32 +43,44 @@ public:
CharT* egptr() const {return base::egptr();}
};
struct Int {
int value;
template <class CharT>
friend void operator>>(std::basic_istream<CharT>& is, Int& self) {
is >> self.value;
}
};
struct A{};
struct A { };
bool called = false;
void operator>>(std::istream&, A&&){ called = true; }
void operator>>(std::istream&, A&&) { called = true; }
int main(int, char**)
{
{
testbuf<char> sb(" 123");
int i = 0;
std::istream(&sb) >> i;
assert(i == 123);
Int i = {0};
std::istream is(&sb);
std::istream&& result = (std::move(is) >> i);
assert(&result == &is);
assert(i.value == 123);
}
{
testbuf<wchar_t> sb(L" 123");
int i = 0;
std::wistream(&sb) >> i;
assert(i == 123);
Int i = {0};
std::wistream is(&sb);
std::wistream&& result = (std::move(is) >> i);
assert(&result == &is);
assert(i.value == 123);
}
{ // test perfect forwarding
{
// test perfect forwarding
assert(called == false);
std::istringstream ss;
auto&& out = (std::move(ss) >> A{});
assert(&out == &ss);
std::istringstream&& result = (std::move(ss) >> A{});
assert(&result == &ss);
assert(called);
}
return 0;
return 0;
}

View File

@@ -0,0 +1,19 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
// Make sure the rvalue overload of operator<< isn't part of the overload set
// when the type is not output streamable into a lvalue stream.
#include <ostream>
#include <utility>
struct Foo { };
using X = decltype(std::declval<std::ostream>() << std::declval<Foo const&>()); // expected-error {{invalid operands to binary expression}}

View File

@@ -10,12 +10,8 @@
// <ostream>
// template <class charT, class traits = char_traits<charT> >
// class basic_ostream;
// template <class charT, class traits, class T>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>&& os, const T& x);
// template <class Stream, class T>
// Stream&& operator<<(Stream&& os, const T& x);
#include <ostream>
#include <cassert>
@@ -55,19 +51,32 @@ protected:
}
};
struct Int {
int value;
template <class CharT>
friend void operator<<(std::basic_ostream<CharT>& os, Int const& self) {
os << self.value;
}
};
int main(int, char**)
{
{
testbuf<char> sb;
std::ostream(&sb) << "testing...";
assert(sb.str() == "testing...");
std::ostream os(&sb);
Int const i = {123};
std::ostream&& result = (std::move(os) << i);
assert(&result == &os);
assert(sb.str() == "123");
}
{
testbuf<wchar_t> sb;
std::wostream(&sb) << L"123";
std::wostream os(&sb);
Int const i = {123};
std::wostream&& result = (std::move(os) << i);
assert(&result == &os);
assert(sb.str() == L"123");
}
return 0;
return 0;
}

View File

@@ -468,7 +468,7 @@
<tr><td><a href="https://wg21.link/LWG3149">3149</a></td><td><tt>DefaultConstructible</tt> should require default initialization</td><td>Belfast</td><td></td></tr>
<tr><td></td><td></td><td></td><td></td></tr> -->
<tr><td><a href="https://wg21.link/LWG1203">1203</a></td><td>More useful rvalue stream insertion</td><td>Prague</td><td></td></tr>
<tr><td><a href="https://wg21.link/LWG1203">1203</a></td><td>More useful rvalue stream insertion</td><td>Prague</td><td>12.0</td></tr>
<tr><td><a href="https://wg21.link/LWG2859">2859</a></td><td>Definition of <em>reachable</em> in [ptr.launder] misses pointer arithmetic from pointer-interconvertible object</td><td>Prague</td><td></td></tr>
<tr><td><a href="https://wg21.link/LWG3018">3018</a></td><td><tt>shared_ptr</tt> of function type</td><td>Prague</td><td></td></tr>
<tr><td><a href="https://wg21.link/LWG3050">3050</a></td><td>Conversion specification problem in <tt>chrono::duration</tt> constructor</td><td>Prague</td><td></td></tr>