Fix PR27374 - Remove the implicit reduced-arity-extension in tuple.
This patch removes libc++'s tuple extension which allowed it to be constructed from fewer initializers than elements; with the remaining elements being default constructed. However the implicit version of this extension breaks conforming code. For example: int fun(std::string); int fun(std::tuple<std::string, int>); int x = fun("hello"); // ambigious Because existing code may already depend on this extension it can be re-enabled by defining _LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION. Note that the explicit version of this extension is still supported, although it's somewhat less useful than the implicit one. llvm-svn: 289158
This commit is contained in:
parent
568196bf7b
commit
bd688258ba
|
@ -155,3 +155,30 @@ thread safety annotations.
|
|||
Defining this macro and then building libc++ with hidden visibility gives a
|
||||
build of libc++ which does not export any symbols, which can be useful when
|
||||
building statically for inclusion into another library.
|
||||
|
||||
**_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION**:
|
||||
This macro is used to re-enable an extension in `std::tuple` which allowed
|
||||
it to be implicitly constructed from fewer initializers than contained
|
||||
elements. Elements without an initializer are default constructed. For example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
std::tuple<std::string, int, std::error_code> foo() {
|
||||
return {"hello world", 42}; // default constructs error_code
|
||||
}
|
||||
|
||||
|
||||
Since libc++ 4.0 this extension has been disabled by default. This macro
|
||||
may be defined to re-enable it in order to support existing code that depends
|
||||
on the extension. New use of this extension should be discouraged.
|
||||
See `PR 27374 <http://llvm.org/PR27374>`_ for more information.
|
||||
|
||||
Note: The "reduced-arity-initialization" extension is still offered but only
|
||||
for explicit conversions. Example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
auto foo() {
|
||||
using Tup = std::tuple<std::string, int, std::error_code>;
|
||||
return Tup{"hello world", 42}; // explicit constructor called. OK.
|
||||
}
|
||||
|
|
|
@ -477,6 +477,12 @@ class _LIBCPP_TYPE_VIS_ONLY tuple
|
|||
|
||||
base base_;
|
||||
|
||||
#if defined(_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION)
|
||||
static constexpr bool _EnableImplicitReducedArityExtension = true;
|
||||
#else
|
||||
static constexpr bool _EnableImplicitReducedArityExtension = false;
|
||||
#endif
|
||||
|
||||
template <class ..._Args>
|
||||
struct _PackExpandsToThisTuple : false_type {};
|
||||
|
||||
|
@ -703,11 +709,17 @@ public:
|
|||
) {}
|
||||
|
||||
template <class ..._Up,
|
||||
bool _PackIsTuple = _PackExpandsToThisTuple<_Up...>::value,
|
||||
typename enable_if
|
||||
<
|
||||
_CheckArgsConstructor<
|
||||
sizeof...(_Up) <= sizeof...(_Tp)
|
||||
&& !_PackExpandsToThisTuple<_Up...>::value
|
||||
sizeof...(_Up) == sizeof...(_Tp)
|
||||
&& !_PackIsTuple
|
||||
>::template __enable_implicit<_Up...>() ||
|
||||
_CheckArgsConstructor<
|
||||
_EnableImplicitReducedArityExtension
|
||||
&& sizeof...(_Up) < sizeof...(_Tp)
|
||||
&& !_PackIsTuple
|
||||
>::template __enable_implicit<_Up...>(),
|
||||
bool
|
||||
>::type = false
|
||||
|
@ -735,7 +747,12 @@ public:
|
|||
_CheckArgsConstructor<
|
||||
sizeof...(_Up) <= sizeof...(_Tp)
|
||||
&& !_PackExpandsToThisTuple<_Up...>::value
|
||||
>::template __enable_explicit<_Up...>(),
|
||||
>::template __enable_explicit<_Up...>() ||
|
||||
_CheckArgsConstructor<
|
||||
!_EnableImplicitReducedArityExtension
|
||||
&& sizeof...(_Up) < sizeof...(_Tp)
|
||||
&& !_PackExpandsToThisTuple<_Up...>()
|
||||
>::template __enable_implicit<_Up...>(),
|
||||
bool
|
||||
>::type = false
|
||||
>
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <tuple>
|
||||
|
||||
// template <class... Types> class tuple;
|
||||
|
||||
// template <class... UTypes>
|
||||
// explicit tuple(UTypes&&... u);
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
#include <tuple>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_convertible.hpp"
|
||||
#include "MoveOnly.h"
|
||||
|
||||
#if defined(_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION)
|
||||
#error This macro should not be defined by default
|
||||
#endif
|
||||
|
||||
struct NoDefault { NoDefault() = delete; };
|
||||
|
||||
|
||||
// Make sure the _Up... constructor SFINAEs out when the types that
|
||||
// are not explicitly initialized are not all default constructible.
|
||||
// Otherwise, std::is_constructible would return true but instantiating
|
||||
// the constructor would fail.
|
||||
void test_default_constructible_extension_sfinae()
|
||||
{
|
||||
typedef MoveOnly MO;
|
||||
typedef NoDefault ND;
|
||||
{
|
||||
typedef std::tuple<MO, ND> Tuple;
|
||||
static_assert(!std::is_constructible<Tuple, MO>::value, "");
|
||||
static_assert(std::is_constructible<Tuple, MO, ND>::value, "");
|
||||
static_assert(test_convertible<Tuple, MO, ND>(), "");
|
||||
}
|
||||
{
|
||||
typedef std::tuple<MO, MO, ND> Tuple;
|
||||
static_assert(!std::is_constructible<Tuple, MO, MO>::value, "");
|
||||
static_assert(std::is_constructible<Tuple, MO, MO, ND>::value, "");
|
||||
static_assert(test_convertible<Tuple, MO, MO, ND>(), "");
|
||||
}
|
||||
{
|
||||
// Same idea as above but with a nested tuple type.
|
||||
typedef std::tuple<MO, ND> Tuple;
|
||||
typedef std::tuple<MO, Tuple, MO, MO> NestedTuple;
|
||||
|
||||
static_assert(!std::is_constructible<
|
||||
NestedTuple, MO, MO, MO, MO>::value, "");
|
||||
static_assert(std::is_constructible<
|
||||
NestedTuple, MO, Tuple, MO, MO>::value, "");
|
||||
}
|
||||
}
|
||||
|
||||
using ExplicitTup = std::tuple<std::string, int, std::error_code>;
|
||||
ExplicitTup doc_example() {
|
||||
return ExplicitTup{"hello world", 42}; // explicit constructor called. OK.
|
||||
}
|
||||
|
||||
// Test that the example given in UsingLibcxx.rst actually works.
|
||||
void test_example_from_docs() {
|
||||
auto tup = doc_example();
|
||||
assert(std::get<0>(tup) == "hello world");
|
||||
assert(std::get<1>(tup) == 42);
|
||||
assert(std::get<2>(tup) == std::error_code{});
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
using E = MoveOnly;
|
||||
using Tup = std::tuple<E, E, E>;
|
||||
// Test that the reduced arity initialization extension is only
|
||||
// allowed on the explicit constructor.
|
||||
static_assert(test_convertible<Tup, E, E, E>(), "");
|
||||
|
||||
Tup t(E(0), E(1));
|
||||
static_assert(std::is_constructible<Tup, E, E>::value, "");
|
||||
static_assert(!test_convertible<Tup, E, E>(), "");
|
||||
assert(std::get<0>(t) == 0);
|
||||
assert(std::get<1>(t) == 1);
|
||||
assert(std::get<2>(t) == MoveOnly());
|
||||
|
||||
Tup t2(E(0));
|
||||
static_assert(std::is_constructible<Tup, E>::value, "");
|
||||
static_assert(!test_convertible<Tup, E>(), "");
|
||||
assert(std::get<0>(t) == 0);
|
||||
assert(std::get<1>(t) == E());
|
||||
assert(std::get<2>(t) == E());
|
||||
}
|
||||
// Check that SFINAE is properly applied with the default reduced arity
|
||||
// constructor extensions.
|
||||
test_default_constructible_extension_sfinae();
|
||||
test_example_from_docs();
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <tuple>
|
||||
|
||||
// template <class... Types> class tuple;
|
||||
|
||||
// template <class... UTypes>
|
||||
// explicit tuple(UTypes&&... u);
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// MODULES_DEFINES: _LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION
|
||||
#define _LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION
|
||||
#include <tuple>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_convertible.hpp"
|
||||
#include "MoveOnly.h"
|
||||
|
||||
|
||||
struct NoDefault { NoDefault() = delete; };
|
||||
|
||||
|
||||
// Make sure the _Up... constructor SFINAEs out when the types that
|
||||
// are not explicitly initialized are not all default constructible.
|
||||
// Otherwise, std::is_constructible would return true but instantiating
|
||||
// the constructor would fail.
|
||||
void test_default_constructible_extension_sfinae()
|
||||
{
|
||||
typedef MoveOnly MO;
|
||||
typedef NoDefault ND;
|
||||
{
|
||||
typedef std::tuple<MO, ND> Tuple;
|
||||
static_assert(!std::is_constructible<Tuple, MO>::value, "");
|
||||
static_assert(std::is_constructible<Tuple, MO, ND>::value, "");
|
||||
static_assert(test_convertible<Tuple, MO, ND>(), "");
|
||||
}
|
||||
{
|
||||
typedef std::tuple<MO, MO, ND> Tuple;
|
||||
static_assert(!std::is_constructible<Tuple, MO, MO>::value, "");
|
||||
static_assert(std::is_constructible<Tuple, MO, MO, ND>::value, "");
|
||||
static_assert(test_convertible<Tuple, MO, MO, ND>(), "");
|
||||
}
|
||||
{
|
||||
// Same idea as above but with a nested tuple type.
|
||||
typedef std::tuple<MO, ND> Tuple;
|
||||
typedef std::tuple<MO, Tuple, MO, MO> NestedTuple;
|
||||
|
||||
static_assert(!std::is_constructible<
|
||||
NestedTuple, MO, MO, MO, MO>::value, "");
|
||||
static_assert(std::is_constructible<
|
||||
NestedTuple, MO, Tuple, MO, MO>::value, "");
|
||||
}
|
||||
{
|
||||
typedef std::tuple<MO, int> Tuple;
|
||||
typedef std::tuple<MO, Tuple, MO, MO> NestedTuple;
|
||||
|
||||
static_assert(std::is_constructible<
|
||||
NestedTuple, MO, MO, MO, MO>::value, "");
|
||||
static_assert(test_convertible<
|
||||
NestedTuple, MO, MO, MO, MO>(), "");
|
||||
|
||||
static_assert(std::is_constructible<
|
||||
NestedTuple, MO, Tuple, MO, MO>::value, "");
|
||||
static_assert(test_convertible<
|
||||
NestedTuple, MO, Tuple, MO, MO>(), "");
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::string, int, std::error_code> doc_example() {
|
||||
return {"hello world", 42};
|
||||
}
|
||||
|
||||
// Test that the example given in UsingLibcxx.rst actually works.
|
||||
void test_example_from_docs() {
|
||||
auto tup = doc_example();
|
||||
assert(std::get<0>(tup) == "hello world");
|
||||
assert(std::get<1>(tup) == 42);
|
||||
assert(std::get<2>(tup) == std::error_code{});
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
{
|
||||
using E = MoveOnly;
|
||||
using Tup = std::tuple<E, E, E>;
|
||||
static_assert(test_convertible<Tup, E, E, E>(), "");
|
||||
|
||||
Tup t = {E(0), E(1)};
|
||||
static_assert(test_convertible<Tup, E, E>(), "");
|
||||
assert(std::get<0>(t) == 0);
|
||||
assert(std::get<1>(t) == 1);
|
||||
assert(std::get<2>(t) == MoveOnly());
|
||||
|
||||
Tup t2 = {E(0)};
|
||||
static_assert(test_convertible<Tup, E>(), "");
|
||||
assert(std::get<0>(t) == 0);
|
||||
assert(std::get<1>(t) == E());
|
||||
assert(std::get<2>(t) == E());
|
||||
}
|
||||
// Check that SFINAE is properly applied with the default reduced arity
|
||||
// constructor extensions.
|
||||
test_default_constructible_extension_sfinae();
|
||||
test_example_from_docs();
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_convertible.hpp"
|
||||
#include "MoveOnly.h"
|
||||
|
||||
#if TEST_STD_VER > 11
|
||||
|
@ -124,17 +125,23 @@ int main()
|
|||
// extensions
|
||||
#ifdef _LIBCPP_VERSION
|
||||
{
|
||||
std::tuple<MoveOnly, MoveOnly, MoveOnly> t(MoveOnly(0),
|
||||
MoveOnly(1));
|
||||
using E = MoveOnly;
|
||||
using Tup = std::tuple<E, E, E>;
|
||||
// Test that the reduced arity initialization extension is only
|
||||
// allowed on the explicit constructor.
|
||||
static_assert(test_convertible<Tup, E, E, E>(), "");
|
||||
|
||||
Tup t(E(0), E(1));
|
||||
static_assert(!test_convertible<Tup, E, E>(), "");
|
||||
assert(std::get<0>(t) == 0);
|
||||
assert(std::get<1>(t) == 1);
|
||||
assert(std::get<2>(t) == MoveOnly());
|
||||
}
|
||||
{
|
||||
std::tuple<MoveOnly, MoveOnly, MoveOnly> t(MoveOnly(0));
|
||||
|
||||
Tup t2(E(0));
|
||||
static_assert(!test_convertible<Tup, E>(), "");
|
||||
assert(std::get<0>(t) == 0);
|
||||
assert(std::get<1>(t) == MoveOnly());
|
||||
assert(std::get<2>(t) == MoveOnly());
|
||||
assert(std::get<1>(t) == E());
|
||||
assert(std::get<2>(t) == E());
|
||||
}
|
||||
#endif
|
||||
#if TEST_STD_VER > 11
|
||||
|
|
Loading…
Reference in New Issue