Skip to content

Commit d2cb198

Browse files
mkurdejldionne
andcommitted
[libc++] Make future_error constructor standard-compliant
This patch removes the non compliant constructor of std::future_error and adds the standards compliant constructor in C++17 instead. Note that we can't support the constructor as an extension in all standard modes because it uses delegating constructors, which require C++11. We could in theory support the constructor as an extension in C++11 and C++14 only, however I believe it is acceptable not to do that since I expect the breakage from this patch will be minimal. If it turns out that more code than we expect is broken by this, we can reconsider that decision. This was found during D99515. Differential Revision: https://reviews.llvm.org/D99567 Co-authored-by: Louis Dionne <[email protected]>
1 parent 2c49311 commit d2cb198

File tree

8 files changed

+143
-94
lines changed

8 files changed

+143
-94
lines changed

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ Deprecations and Removals
8787
warning). ``_LIBCPP_ENABLE_ASSERTIONS`` will be removed entirely in the next release and setting it will become an
8888
error. See :ref:`the hardening documentation <using-hardening-modes>` for more details.
8989

90+
- The non-conforming constructor ``std::future_error(std::error_code)`` has been removed. Please use the
91+
``std::future_error(std::future_errc)`` constructor provided in C++17 instead.
92+
9093
Upcoming Deprecations and Removals
9194
----------------------------------
9295

libcxx/include/future

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,15 @@ error_condition make_error_condition(future_errc e) noexcept;
4444
4545
const error_category& future_category() noexcept;
4646
47-
class future_error
48-
: public logic_error
49-
{
47+
class future_error : public logic_error {
5048
public:
51-
future_error(error_code ec); // exposition only
52-
explicit future_error(future_errc); // C++17
49+
explicit future_error(future_errc e); // since C++17
50+
5351
const error_code& code() const noexcept;
5452
const char* what() const noexcept;
53+
54+
private:
55+
error_code ec_; // exposition only
5556
};
5657
5758
template <class R>
@@ -516,12 +517,25 @@ make_error_condition(future_errc __e) _NOEXCEPT
516517
return error_condition(static_cast<int>(__e), future_category());
517518
}
518519

520+
_LIBCPP_NORETURN inline _LIBCPP_HIDE_FROM_ABI
521+
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
522+
_LIBCPP_AVAILABILITY_FUTURE_ERROR
523+
#endif
524+
void __throw_future_error(future_errc __ev);
525+
519526
class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_FUTURE_ERROR future_error
520527
: public logic_error
521528
{
522529
error_code __ec_;
530+
531+
future_error(error_code);
532+
friend void __throw_future_error(future_errc);
533+
template <class> friend class promise;
534+
523535
public:
524-
future_error(error_code __ec);
536+
#if _LIBCPP_STD_VER >= 17
537+
_LIBCPP_HIDE_FROM_ABI explicit future_error(future_errc __ec) : future_error(std::make_error_code(__ec)) {}
538+
#endif
525539

526540
_LIBCPP_INLINE_VISIBILITY
527541
const error_code& code() const _NOEXCEPT {return __ec_;}
@@ -530,10 +544,7 @@ public:
530544
~future_error() _NOEXCEPT override;
531545
};
532546

533-
_LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY
534-
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
535-
_LIBCPP_AVAILABILITY_FUTURE_ERROR
536-
#endif
547+
// Declared above std::future_error
537548
void __throw_future_error(future_errc __ev)
538549
{
539550
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
@@ -1359,9 +1370,7 @@ promise<_Rp>::~promise()
13591370
if (__state_)
13601371
{
13611372
if (!__state_->__has_value() && __state_->use_count() > 1)
1362-
__state_->set_exception(make_exception_ptr(
1363-
future_error(make_error_code(future_errc::broken_promise))
1364-
));
1373+
__state_->set_exception(make_exception_ptr(future_error(make_error_code(future_errc::broken_promise))));
13651374
__state_->__release_shared();
13661375
}
13671376
}
@@ -1502,9 +1511,7 @@ promise<_Rp&>::~promise()
15021511
if (__state_)
15031512
{
15041513
if (!__state_->__has_value() && __state_->use_count() > 1)
1505-
__state_->set_exception(make_exception_ptr(
1506-
future_error(make_error_code(future_errc::broken_promise))
1507-
));
1514+
__state_->set_exception(make_exception_ptr(future_error(make_error_code(future_errc::broken_promise))));
15081515
__state_->__release_shared();
15091516
}
15101517
}

libcxx/src/future.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,7 @@ promise<void>::~promise()
204204
{
205205
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
206206
if (!__state_->__has_value() && __state_->use_count() > 1)
207-
__state_->set_exception(make_exception_ptr(
208-
future_error(make_error_code(future_errc::broken_promise))
209-
));
207+
__state_->set_exception(make_exception_ptr(future_error(future_errc::broken_promise)));
210208
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
211209
__state_->__release_shared();
212210
}

libcxx/test/std/thread/futures/futures.future_error/code.pass.cpp

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,43 @@
99
// UNSUPPORTED: no-threads
1010

1111
// <future>
12-
12+
//
1313
// class future_error
14-
// future_error(error_code __ec); // exposition only
15-
// explicit future_error(future_errc _Ev) : __ec_(make_error_code(_Ev)) {} // C++17
16-
17-
// const error_code& code() const throw();
14+
//
15+
// const error_code& code() const noexcept;
1816

19-
#include <future>
2017
#include <cassert>
18+
#include <future>
19+
#include <utility>
2120

2221
#include "test_macros.h"
2322

24-
int main(int, char**)
25-
{
26-
{
27-
std::error_code ec = std::make_error_code(std::future_errc::broken_promise);
28-
std::future_error f(ec);
29-
assert(f.code() == ec);
30-
}
31-
{
32-
std::error_code ec = std::make_error_code(std::future_errc::future_already_retrieved);
33-
std::future_error f(ec);
34-
assert(f.code() == ec);
35-
}
36-
{
37-
std::error_code ec = std::make_error_code(std::future_errc::promise_already_satisfied);
38-
std::future_error f(ec);
39-
assert(f.code() == ec);
40-
}
41-
{
42-
std::error_code ec = std::make_error_code(std::future_errc::no_state);
43-
std::future_error f(ec);
44-
assert(f.code() == ec);
45-
}
46-
#if TEST_STD_VER > 14
47-
{
48-
std::future_error f(std::future_errc::broken_promise);
49-
assert(f.code() == std::make_error_code(std::future_errc::broken_promise));
50-
}
51-
{
52-
std::future_error f(std::future_errc::no_state);
53-
assert(f.code() == std::make_error_code(std::future_errc::no_state));
54-
}
23+
int main(int, char**) {
24+
ASSERT_NOEXCEPT(std::declval<std::future_error const&>().code());
25+
ASSERT_SAME_TYPE(decltype(std::declval<std::future_error const&>().code()), std::error_code const&);
26+
27+
// Before C++17, we can't construct std::future_error directly in a standards-conforming way
28+
#if TEST_STD_VER >= 17
29+
{
30+
std::future_error const f(std::future_errc::broken_promise);
31+
std::error_code const& code = f.code();
32+
assert(code == std::make_error_code(std::future_errc::broken_promise));
33+
}
34+
{
35+
std::future_error const f(std::future_errc::future_already_retrieved);
36+
std::error_code const& code = f.code();
37+
assert(code == std::make_error_code(std::future_errc::future_already_retrieved));
38+
}
39+
{
40+
std::future_error const f(std::future_errc::promise_already_satisfied);
41+
std::error_code const& code = f.code();
42+
assert(code == std::make_error_code(std::future_errc::promise_already_satisfied));
43+
}
44+
{
45+
std::future_error const f(std::future_errc::no_state);
46+
std::error_code const& code = f.code();
47+
assert(code == std::make_error_code(std::future_errc::no_state));
48+
}
5549
#endif
5650

5751
return 0;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// UNSUPPORTED: no-threads
10+
// UNSUPPORTED: c++03, c++11, c++14
11+
12+
// <future>
13+
14+
// class future_error
15+
//
16+
// explicit future_error(future_errc e); // since C++17
17+
18+
#include <cassert>
19+
#include <future>
20+
#include <type_traits>
21+
22+
int main(int, char**) {
23+
{
24+
std::future_error f(std::future_errc::broken_promise);
25+
assert(f.code() == std::make_error_code(std::future_errc::broken_promise));
26+
}
27+
{
28+
std::future_error f(std::future_errc::future_already_retrieved);
29+
assert(f.code() == std::make_error_code(std::future_errc::future_already_retrieved));
30+
}
31+
{
32+
std::future_error f(std::future_errc::promise_already_satisfied);
33+
assert(f.code() == std::make_error_code(std::future_errc::promise_already_satisfied));
34+
}
35+
{
36+
std::future_error f(std::future_errc::no_state);
37+
assert(f.code() == std::make_error_code(std::future_errc::no_state));
38+
}
39+
40+
// Make sure the constructor is explicit
41+
static_assert(!std::is_convertible_v<std::future_errc, std::future_error>);
42+
43+
return 0;
44+
}

libcxx/test/std/thread/futures/futures.future_error/types.pass.cpp renamed to libcxx/test/std/thread/futures/futures.future_error/types.compile.pass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,4 @@
1515
#include <future>
1616
#include <type_traits>
1717

18-
#include "test_macros.h"
19-
20-
int main(int, char**)
21-
{
22-
static_assert((std::is_convertible<std::future_error*,
23-
std::logic_error*>::value), "");
24-
25-
return 0;
26-
}
18+
static_assert(std::is_convertible<std::future_error*, std::logic_error*>::value, "");

libcxx/test/std/thread/futures/futures.future_error/what.pass.cpp

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,53 @@
1313
//
1414
// XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{9|10|11}}
1515

16-
// <future>
16+
// VC Runtime's std::exception::what() method is not marked as noexcept, so
17+
// this fails.
18+
// UNSUPPORTED: target=x86_64-pc-windows-msvc
1719

20+
// <future>
21+
//
1822
// class future_error
23+
//
24+
// const char* what() const noexcept;
1925

20-
// const char* what() const throw();
21-
22-
#include <future>
23-
#include <cstring>
2426
#include <cassert>
27+
#include <future>
28+
#include <string_view>
29+
#include <utility>
2530

2631
#include "test_macros.h"
2732

28-
int main(int, char**)
29-
{
30-
{
31-
std::future_error f(std::make_error_code(std::future_errc::broken_promise));
32-
LIBCPP_ASSERT(std::strcmp(f.what(), "The associated promise has been destructed prior "
33-
"to the associated state becoming ready.") == 0);
34-
}
35-
{
36-
std::future_error f(std::make_error_code(std::future_errc::future_already_retrieved));
37-
LIBCPP_ASSERT(std::strcmp(f.what(), "The future has already been retrieved from "
38-
"the promise or packaged_task.") == 0);
39-
}
40-
{
41-
std::future_error f(std::make_error_code(std::future_errc::promise_already_satisfied));
42-
LIBCPP_ASSERT(std::strcmp(f.what(), "The state of the promise has already been set.") == 0);
43-
}
44-
{
45-
std::future_error f(std::make_error_code(std::future_errc::no_state));
46-
LIBCPP_ASSERT(std::strcmp(f.what(), "Operation not permitted on an object without "
47-
"an associated state.") == 0);
48-
}
33+
int main(int, char**) {
34+
ASSERT_NOEXCEPT(std::declval<std::future_error const&>().what());
35+
ASSERT_SAME_TYPE(decltype(std::declval<std::future_error const&>().what()), char const*);
36+
37+
// Before C++17, we can't construct std::future_error directly in a standards-conforming way
38+
#if TEST_STD_VER >= 17
39+
{
40+
std::future_error const f(std::future_errc::broken_promise);
41+
char const* what = f.what();
42+
LIBCPP_ASSERT(what == std::string_view{"The associated promise has been destructed prior "
43+
"to the associated state becoming ready."});
44+
}
45+
{
46+
std::future_error f(std::future_errc::future_already_retrieved);
47+
char const* what = f.what();
48+
LIBCPP_ASSERT(what == std::string_view{"The future has already been retrieved from "
49+
"the promise or packaged_task."});
50+
}
51+
{
52+
std::future_error f(std::future_errc::promise_already_satisfied);
53+
char const* what = f.what();
54+
LIBCPP_ASSERT(what == std::string_view{"The state of the promise has already been set."});
55+
}
56+
{
57+
std::future_error f(std::future_errc::no_state);
58+
char const* what = f.what();
59+
LIBCPP_ASSERT(what == std::string_view{"Operation not permitted on an object without "
60+
"an associated state."});
61+
}
62+
#endif
4963

5064
return 0;
5165
}

libcxx/utils/data/ignore_format.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5614,9 +5614,6 @@ libcxx/test/std/thread/futures/futures.errors/equivalent_int_error_condition.pas
56145614
libcxx/test/std/thread/futures/futures.errors/future_category.pass.cpp
56155615
libcxx/test/std/thread/futures/futures.errors/make_error_code.pass.cpp
56165616
libcxx/test/std/thread/futures/futures.errors/make_error_condition.pass.cpp
5617-
libcxx/test/std/thread/futures/futures.future_error/code.pass.cpp
5618-
libcxx/test/std/thread/futures/futures.future_error/types.pass.cpp
5619-
libcxx/test/std/thread/futures/futures.future_error/what.pass.cpp
56205617
libcxx/test/std/thread/futures/futures.overview/future_errc.pass.cpp
56215618
libcxx/test/std/thread/futures/futures.overview/future_status.pass.cpp
56225619
libcxx/test/std/thread/futures/futures.overview/is_error_code_enum_future_errc.pass.cpp

0 commit comments

Comments
 (0)