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. Tested powerpc64le-linux, committed to trunk.
commit d883c22939e8945eb09ec0fb65171f8f161bf142 Author: Jonathan Wakely <jwak...@redhat.com> Date: Fri Feb 23 21:26:08 2018 +0000 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. diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index 1cabd6ae0e6..61861b58520 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -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 diff --git a/libstdc++-v3/testsuite/30_threads/thread/cons/84535.cc b/libstdc++-v3/testsuite/30_threads/thread/cons/84535.cc new file mode 100644 index 00000000000..c96929b6873 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/thread/cons/84535.cc @@ -0,0 +1,31 @@ +// { 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, "");