Skip to content

Commit c4e70c0

Browse files
committed
Revert the last few commits, and formally ban make_status_code()
taking a `status_code`. The previous code worked just fine on most recent compilers, but triggered recursive ADL lookup bugs on older compilers. Most unfortunately, AppleClang still has the historical recursive ADL lookup bug, which means the recent commits broke AppleClang completely. Therefore, with much regret, I'll have to ban the make status code ADL customisation point being able to take status codes as input. This will fix AppleClang and older compiler support.
1 parent 67d289d commit c4e70c0

File tree

7 files changed

+443
-429
lines changed

7 files changed

+443
-429
lines changed

include/status-code/errored_status_code.hpp

Lines changed: 173 additions & 67 deletions
Large diffs are not rendered by default.

include/status-code/generic_code.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, //
478478
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
479479
SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code<DomainType1> &a, const T &b)
480480
{
481+
static_assert(!is_status_code<typename std::decay<T>::type>::value,
482+
"make_status_code() cannot consume status codes!");
481483
return a.equivalent(make_status_code(b));
482484
}
483485
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
@@ -487,6 +489,8 @@ SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, //
487489
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
488490
SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const T &a, const status_code<DomainType1> &b)
489491
{
492+
static_assert(!is_status_code<typename std::decay<T>::type>::value,
493+
"make_status_code() cannot consume status codes!");
490494
return b.equivalent(make_status_code(a));
491495
}
492496
//! True if the status code's are not semantically equal via `equivalent()` to `make_status_code(T)`.
@@ -496,6 +500,8 @@ SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, //
496500
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
497501
SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code<DomainType1> &a, const T &b)
498502
{
503+
static_assert(!is_status_code<typename std::decay<T>::type>::value,
504+
"make_status_code() cannot consume status codes!");
499505
return !a.equivalent(make_status_code(b));
500506
}
501507
//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`.
@@ -505,6 +511,8 @@ SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, //
505511
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code<MakeStatusCodeResult>::value)) // ADL makes a status code
506512
SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const status_code<DomainType1> &b)
507513
{
514+
static_assert(!is_status_code<typename std::decay<T>::type>::value,
515+
"make_status_code() cannot consume status codes!");
508516
return !b.equivalent(make_status_code(a));
509517
}
510518
//! True if the status code's are semantically equal via `equivalent()` to

include/status-code/result.hpp

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
116116
#ifdef _MSC_VER
117117
__declspec(noreturn)
118118
#elif defined(__GNUC__) || defined(__clang__)
119-
__attribute__((noreturn))
119+
__attribute__((noreturn))
120120
#endif
121-
void _ub()
121+
void
122+
_ub()
122123
{
123124
assert(false); // NOLINT
124125
#if defined(__GNUC__) || defined(__clang__)
@@ -147,25 +148,33 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
147148

148149
//! Implicit result converting move constructor
149150
SYSTEM_ERROR2_TEMPLATE(class U)
150-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v<U, T>)) constexpr result(result<U> &&o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
151+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v<U, T>))
152+
constexpr result(result<U> &&o,
153+
_implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
151154
: _base(std::move(o))
152155
{
153156
}
154157
//! Implicit result converting copy constructor
155158
SYSTEM_ERROR2_TEMPLATE(class U)
156-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v<U, T>)) constexpr result(const result<U> &o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
159+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v<U, T>))
160+
constexpr result(const result<U> &o,
161+
_implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
157162
: _base(o)
158163
{
159164
}
160165
//! Explicit result converting move constructor
161166
SYSTEM_ERROR2_TEMPLATE(class U)
162-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v<T, U>)) constexpr explicit result(result<U> &&o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
167+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v<T, U>))
168+
constexpr explicit result(result<U> &&o,
169+
_explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
163170
: _base(std::move(o))
164171
{
165172
}
166173
//! Explicit result converting copy constructor
167174
SYSTEM_ERROR2_TEMPLATE(class U)
168-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v<T, U>)) constexpr explicit result(const result<U> &o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
175+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v<T, U>))
176+
constexpr explicit result(const result<U> &o,
177+
_explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
169178
: _base(o)
170179
{
171180
}
@@ -180,33 +189,43 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
180189
}
181190

182191
//! Implicit in-place converting error constructor
183-
SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, long = 5) //
184-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> && std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
192+
SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, long = 5) //
193+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> &&
194+
std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
185195
&&std::is_constructible_v<error_type, Arg1, Arg2, Args...>))
186-
constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) noexcept(std::is_nothrow_constructible_v<error_type, Arg1, Arg2, Args...>)
196+
constexpr result(Arg1 &&arg1, Arg2 &&arg2,
197+
Args &&...args) noexcept(std::is_nothrow_constructible_v<error_type, Arg1, Arg2, Args...>)
187198
: _base(std::in_place_index<0>, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...)
188199
{
189200
}
190201

191202
//! Implicit in-place converting value constructor
192-
SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, int = 5) //
193-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> && std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
203+
SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, int = 5) //
204+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> &&
205+
std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
194206
&&std::is_constructible_v<value_type, Arg1, Arg2, Args...>))
195-
constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&...args) noexcept(std::is_nothrow_constructible_v<value_type, Arg1, Arg2, Args...>)
207+
constexpr result(Arg1 &&arg1, Arg2 &&arg2,
208+
Args &&...args) noexcept(std::is_nothrow_constructible_v<value_type, Arg1, Arg2, Args...>)
196209
: _base(std::in_place_index<1>, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...)
197210
{
198211
}
199212

200-
//! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
201-
SYSTEM_ERROR2_TEMPLATE(class U, class... Args, //
202-
class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result<U, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found
203-
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!std::is_same<typename std::decay<U>::type, result>::value // not copy/move of self
204-
&& !std::is_same<typename std::decay<U>::type, value_type>::value // not copy/move of value type
205-
&& is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
206-
&& std::is_constructible<error_type, MakeStatusCodeResult>::value)) // ADLed status code is compatible
207-
constexpr result(U &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<U>(), std::declval<Args>()...))) // NOLINT
213+
//! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a
214+
//! `status_code`. Note that `make_status_code(status_code)` is illegal, hence the static assertion.
215+
SYSTEM_ERROR2_TEMPLATE(class U, class... Args, //
216+
class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result<
217+
U, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found
218+
SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(
219+
!std::is_same<typename std::decay<U>::type, result>::value // not copy/move of self
220+
&& !std::is_same<typename std::decay<U>::type, value_type>::value // not copy/move of value type
221+
&& is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
222+
&& std::is_constructible<error_type, MakeStatusCodeResult>::value)) // ADLed status code is compatible
223+
constexpr result(U &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval<U>(),
224+
std::declval<Args>()...))) // NOLINT
208225
: _base(std::in_place_index<0>, make_status_code(static_cast<U &&>(v), static_cast<Args &&>(args)...))
209226
{
227+
static_assert(!is_status_code<typename std::decay<T>::type>::value,
228+
"make_status_code() cannot consume status codes!");
210229
}
211230

212231
//! Swap with another result
@@ -301,7 +320,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
301320
}
302321

303322
//! Accesses the value, being UB if none exists
304-
constexpr value_type_if_enabled &assume_value() &noexcept
323+
constexpr value_type_if_enabled &assume_value() & noexcept
305324
{
306325
if(!has_value())
307326
{
@@ -310,7 +329,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
310329
return *std::get_if<1>(this);
311330
}
312331
//! Accesses the error, being UB if none exists
313-
constexpr const value_type_if_enabled &assume_value() const &noexcept
332+
constexpr const value_type_if_enabled &assume_value() const & noexcept
314333
{
315334
if(!has_value())
316335
{
@@ -319,7 +338,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
319338
return *std::get_if<1>(this);
320339
}
321340
//! Accesses the error, being UB if none exists
322-
constexpr value_type_if_enabled &&assume_value() &&noexcept
341+
constexpr value_type_if_enabled &&assume_value() && noexcept
323342
{
324343
if(!has_value())
325344
{
@@ -328,7 +347,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
328347
return std::move(*std::get_if<1>(this));
329348
}
330349
//! Accesses the error, being UB if none exists
331-
constexpr const value_type_if_enabled &&assume_value() const &&noexcept
350+
constexpr const value_type_if_enabled &&assume_value() const && noexcept
332351
{
333352
if(!has_value())
334353
{
@@ -338,7 +357,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
338357
}
339358

340359
//! Accesses the error, being UB if none exists
341-
constexpr error_type &assume_error() &noexcept
360+
constexpr error_type &assume_error() & noexcept
342361
{
343362
if(!has_error())
344363
{
@@ -347,7 +366,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
347366
return *std::get_if<0>(this);
348367
}
349368
//! Accesses the error, being UB if none exists
350-
constexpr const error_type &assume_error() const &noexcept
369+
constexpr const error_type &assume_error() const & noexcept
351370
{
352371
if(!has_error())
353372
{
@@ -356,7 +375,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
356375
return *std::get_if<0>(this);
357376
}
358377
//! Accesses the error, being UB if none exists
359-
constexpr error_type &&assume_error() &&noexcept
378+
constexpr error_type &&assume_error() && noexcept
360379
{
361380
if(!has_error())
362381
{
@@ -365,7 +384,7 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
365384
return std::move(*std::get_if<0>(this));
366385
}
367386
//! Accesses the error, being UB if none exists
368-
constexpr const error_type &&assume_error() const &&noexcept
387+
constexpr const error_type &&assume_error() const && noexcept
369388
{
370389
if(!has_error())
371390
{
@@ -376,13 +395,15 @@ template <class T> class result : protected std::variant<SYSTEM_ERROR2_NAMESPACE
376395
};
377396

378397
//! True if the two results compare equal.
379-
template <class T, class U, typename = decltype(std::declval<T>() == std::declval<U>())> constexpr inline bool operator==(const result<T> &a, const result<U> &b) noexcept
398+
template <class T, class U, typename = decltype(std::declval<T>() == std::declval<U>())>
399+
constexpr inline bool operator==(const result<T> &a, const result<U> &b) noexcept
380400
{
381401
const auto &x = a._internal();
382402
return x == b;
383403
}
384404
//! True if the two results compare unequal.
385-
template <class T, class U, typename = decltype(std::declval<T>() != std::declval<U>())> constexpr inline bool operator!=(const result<T> &a, const result<U> &b) noexcept
405+
template <class T, class U, typename = decltype(std::declval<T>() != std::declval<U>())>
406+
constexpr inline bool operator!=(const result<T> &a, const result<U> &b) noexcept
386407
{
387408
const auto &x = a._internal();
388409
return x != b;

0 commit comments

Comments
 (0)