Commit d49b3426 by Jonathan Wakely Committed by Jonathan Wakely

PR libstdc++/84535 constrain std::thread constructor

The standard requires that the std::thread constructor is constrained so
it can't be called with a first argument of type std::thread. The
current implementation only meets that requirement if the constructor is
called with one argument, by using deleted overloads. This uses an
enable_if constraint to enforce the requirement for any number of
arguments.

Also add a static assertion to give a more readable error for invalid
arguments that cannot be invoked. Also simplify _Invoker to reduce the
error cascade for ill-formed instantiations with non-invocable
arguments.

	PR libstdc++/84535
	* include/std/thread (thread::__not_same): New SFINAE helper.
	(thread::thread(_Callable&&, _Args&&...)): Add SFINAE constraint that
	first argument is not a std::thread. Add static assertion to check
	INVOKE expression is valid.
	(thread::thread(thread&), thread::thread(const thread&&)): Remove.
	(thread::_Invoke::_M_invoke, thread::_Invoke::operator()): Use
	__invoke_result for return types and remove exception specifications.
	* testsuite/30_threads/thread/cons/84535.cc: New.

From-SVN: r259893
parent 63f12215
2018-05-03 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/84535
* include/std/thread (thread::__not_same): New SFINAE helper.
(thread::thread(_Callable&&, _Args&&...)): Add SFINAE constraint that
first argument is not a std::thread. Add static assertion to check
INVOKE expression is valid.
(thread::thread(thread&), thread::thread(const thread&&)): Remove.
(thread::_Invoke::_M_invoke, thread::_Invoke::operator()): Use
__invoke_result for return types and remove exception specifications.
* testsuite/30_threads/thread/cons/84535.cc: New.
* include/std/future (__async_result_of): Use __invoke_result instead
of result_of.
......
......@@ -102,21 +102,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
private:
id _M_id;
public:
thread() noexcept = default;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2097. packaged_task constructors should be constrained
thread(thread&) = delete;
thread(const thread&) = delete;
thread(const thread&&) = delete;
template<typename _Tp>
using __not_same = __not_<is_same<__remove_cvref_t<_Tp>, thread>>;
thread(thread&& __t) noexcept
{ swap(__t); }
public:
thread() noexcept = default;
template<typename _Callable, typename... _Args>
template<typename _Callable, typename... _Args,
typename = _Require<__not_same<_Callable>>>
explicit
thread(_Callable&& __f, _Args&&... __args)
{
static_assert( __is_invocable<typename decay<_Callable>::type,
typename decay<_Args>::type...>::value,
"std::thread arguments must be invocable after conversion to rvalues"
);
#ifdef GTHR_ACTIVE_PROXY
// Create a reference to pthread_create, not just the gthr weak symbol.
auto __depend = reinterpret_cast<void(*)()>(&pthread_create);
......@@ -135,6 +138,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::terminate();
}
thread(const thread&) = delete;
thread(thread&& __t) noexcept
{ swap(__t); }
thread& operator=(const thread&) = delete;
thread& operator=(thread&& __t) noexcept
......@@ -222,29 +230,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
_Tuple _M_t;
template<size_t _Index>
static __tuple_element_t<_Index, _Tuple>&&
_S_declval();
template<typename>
struct __result;
template<typename _Fn, typename... _Args>
struct __result<tuple<_Fn, _Args...>>
: __invoke_result<_Fn, _Args...>
{ };
template<size_t... _Ind>
auto
typename __result<_Tuple>::type
_M_invoke(_Index_tuple<_Ind...>)
noexcept(noexcept(std::__invoke(_S_declval<_Ind>()...)))
-> decltype(std::__invoke(_S_declval<_Ind>()...))
{ return std::__invoke(std::get<_Ind>(std::move(_M_t))...); }
using _Indices
= typename _Build_index_tuple<tuple_size<_Tuple>::value>::__type;
auto
typename __result<_Tuple>::type
operator()()
noexcept(noexcept(std::declval<_Invoker&>()._M_invoke(_Indices())))
-> decltype(std::declval<_Invoker&>()._M_invoke(_Indices()))
{ return _M_invoke(_Indices()); }
{
using _Indices
= typename _Build_index_tuple<tuple_size<_Tuple>::value>::__type;
return _M_invoke(_Indices());
}
};
template<typename... _Tp>
using __decayed_tuple = tuple<typename std::decay<_Tp>::type...>;
using __decayed_tuple = tuple<typename decay<_Tp>::type...>;
public:
// Returns a call wrapper that stores
......
// { dg-do compile { target c++11 } }
// { dg-require-cstdint "" }
// { dg-require-gthreads "" }
// Copyright (C) 2018 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <thread>
using std::is_constructible;
using std::thread;
// PR libstdc++/84535
static_assert(!is_constructible<thread, thread, int>::value, "");
static_assert(!is_constructible<thread, thread&, int>::value, "");
static_assert(!is_constructible<thread, const thread&, int>::value, "");
static_assert(!is_constructible<thread, const thread&&, int>::value, "");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment