Skip to content

Commit 4b1b255

Browse files
committed
Finish unifying the built-in "as" functions to remove the non-const overload
See discussion in #1057 Closes #961 Closes #1057 - with thanks to @soukatch for pointing out the offending code region to narrow the bug diagnosis, appreciated! Closes #1061
1 parent 001a1a1 commit 4b1b255

File tree

3 files changed

+51
-40
lines changed

3 files changed

+51
-40
lines changed

Diff for: include/cpp2util.h

+45-34
Original file line numberDiff line numberDiff line change
@@ -1467,8 +1467,27 @@ inline constexpr auto as() -> auto
14671467
}
14681468
}
14691469

1470-
template< typename C, typename X >
1471-
auto as(X const& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) {
1470+
template< typename C, auto x >
1471+
requires (std::is_same_v<C, std::string> && std::is_integral_v<CPP2_TYPEOF(x)>)
1472+
inline constexpr auto as() -> auto
1473+
{
1474+
return cpp2::to_string(CPP2_FORWARD(x));
1475+
}
1476+
1477+
template< typename C >
1478+
auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
1479+
// This "requires" list may need to be tweaked further. The idea is to have
1480+
// this function used for all the cases it's supposed to cover, but not
1481+
// hide user-supplied extensions (such as the ones later in this file for
1482+
// std:: polymorphic types like any/optional/variant)
1483+
requires (
1484+
(std::is_scalar_v<CPP2_TYPEOF(x)> && !std::is_enum_v<CPP2_TYPEOF(x)>)
1485+
|| std::is_floating_point_v<CPP2_TYPEOF(x)>
1486+
|| std::is_base_of_v<C, CPP2_TYPEOF(x)>
1487+
|| std::is_base_of_v<CPP2_TYPEOF(x), C>
1488+
|| requires { C{CPP2_FORWARD(x)}; }
1489+
)
1490+
{
14721491
if constexpr (
14731492
std::is_floating_point_v<C> &&
14741493
std::is_floating_point_v<CPP2_TYPEOF(x)> &&
@@ -1487,65 +1506,57 @@ auto as(X const& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) {
14871506
sizeof(CPP2_TYPEOF(x)) <= sizeof(C)
14881507
)
14891508
{
1490-
const C c = static_cast<C>(x);
1509+
const C c = static_cast<C>(CPP2_FORWARD(x));
14911510
type_safety.enforce( // precondition check: must be round-trippable => not lossy
14921511
static_cast<CPP2_TYPEOF(x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF(x){}),
14931512
"dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
14941513
);
14951514
return CPP2_COPY(c);
14961515
}
1497-
else if constexpr (std::is_same_v<C, std::string> && std::is_integral_v<X>) {
1498-
return cpp2::to_string(x);
1516+
else if constexpr (std::is_same_v<C, std::string> && std::is_integral_v<CPP2_TYPEOF(x)>) {
1517+
return cpp2::to_string(CPP2_FORWARD(x));
14991518
}
1500-
else if constexpr (std::is_same_v<C, X>) {
1501-
return x;
1519+
else if constexpr (std::is_same_v<C, CPP2_TYPEOF(x)>) {
1520+
return CPP2_FORWARD(x);
15021521
}
1503-
else if constexpr (std::is_base_of_v<C, X>) {
1504-
return static_cast<C const&>(x);
1522+
else if constexpr (std::is_base_of_v<C, CPP2_TYPEOF(x)>) {
1523+
if constexpr (std::is_const_v<std::remove_reference_t<decltype(x)>>) {
1524+
return static_cast<C const&>(CPP2_FORWARD(x));
1525+
} else {
1526+
return static_cast<C&>(CPP2_FORWARD(x));
1527+
}
15051528
}
1506-
else if constexpr (std::is_base_of_v<X, C>) {
1507-
return Dynamic_cast<C const&>(x);
1529+
else if constexpr (std::is_base_of_v<CPP2_TYPEOF(x), C>) {
1530+
if constexpr (std::is_const_v<std::remove_reference_t<decltype(x)>>) {
1531+
return Dynamic_cast<C const&>(CPP2_FORWARD(x));
1532+
} else {
1533+
return Dynamic_cast<C&>(CPP2_FORWARD(x));
1534+
}
15081535
}
15091536
else if constexpr (
15101537
std::is_pointer_v<C>
1511-
&& std::is_pointer_v<X>
1512-
&& requires { requires std::is_base_of_v<deref_t<X>, deref_t<C>>; }
1538+
&& std::is_pointer_v<CPP2_TYPEOF(x)>
1539+
&& requires { requires std::is_base_of_v<deref_t<CPP2_TYPEOF(x)>, deref_t<C>>; }
15131540
)
15141541
{
1515-
return Dynamic_cast<C>(x);
1542+
return Dynamic_cast<C>(CPP2_FORWARD(x));
15161543
}
1517-
else if constexpr (requires { C{x}; }) {
1544+
else if constexpr (requires { C{CPP2_FORWARD(x)}; }) {
15181545
// Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
15191546
// like std::optional, and try to prevent accidental narrowing conversions even when
15201547
// those types themselves don't defend against them
1521-
if constexpr( requires { requires std::is_convertible_v<X, typename C::value_type>; } ) {
1522-
if constexpr( is_narrowing_v<typename C::value_type, X>) {
1548+
if constexpr( requires { requires std::is_convertible_v<CPP2_TYPEOF(x), typename C::value_type>; } ) {
1549+
if constexpr( is_narrowing_v<typename C::value_type, CPP2_TYPEOF(x)>) {
15231550
return nonesuch;
15241551
}
15251552
}
1526-
return C{x};
1553+
return C{CPP2_FORWARD(x)};
15271554
}
15281555
else {
15291556
return nonesuch;
15301557
}
15311558
}
15321559

1533-
template< typename C, typename X >
1534-
auto as( X& x ) -> decltype(auto) {
1535-
if constexpr (std::is_same_v<C, X>) {
1536-
return x;
1537-
}
1538-
else if constexpr (std::is_base_of_v<C, X>) {
1539-
return static_cast<C&>(x);
1540-
}
1541-
else if constexpr (std::is_base_of_v<X, C>) {
1542-
return Dynamic_cast<C&>(x);
1543-
}
1544-
else {
1545-
return as<C>(std::as_const(x));
1546-
}
1547-
}
1548-
15491560

15501561
//-------------------------------------------------------------------------------------------------------------
15511562
// std::variant is and as

Diff for: regression-tests/test-results/gcc-10/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expect
66
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
77
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
88
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
9-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:91&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type>’
9+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:92&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type>’
1010
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:11: note: candidates are: ‘element::element(const element&)’
11-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:89> element::element(auto:89&&)’
11+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:90> element::element(auto:90&&)’
1212
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here
1313
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:78: error: expected unqualified-id before ‘{’ token
14-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:92&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type>’
14+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:93&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type>’
1515
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:6:16: note: candidates are: ‘void element::operator=(const element&)’
16-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:90> element& element::operator=(auto:90&&)’
16+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:91> element& element::operator=(auto:91&&)’
1717
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here

Diff for: regression-tests/test-results/gcc-10/pure2-print.cpp.output

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ pure2-print.cpp2:68:1: note: in expansion of macro ‘CPP2_REQUIRES_’
99
pure2-print.cpp2:97:1: note: in expansion of macro ‘CPP2_REQUIRES_’
1010
pure2-print.cpp2:9:41: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’
1111
pure2-print.cpp2:9:48: error: template definition of non-template ‘constexpr const T outer::object_alias’
12-
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:90& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13-
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:89> static void outer::mytype::variadic(const auto:89& ...)’
12+
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:91& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13+
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:90> static void outer::mytype::variadic(const auto:90& ...)’
1414
pure2-print.cpp2:10:19: note: ‘class outer::mytype’ defined here
1515
pure2-print.cpp2:96:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
1616
pure2-print.cpp2:96:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’

0 commit comments

Comments
 (0)