[libc++][ranges] Reject non-class types in ranges::to (#135802)

This patch adds `static_assert` using `is_class_v` and `is_union_v` to
reject no-class type template parameters.

Fixes #132133

---------

Co-authored-by: A. Jiang <de34@live.cn>
This commit is contained in:
Yuzhiy
2025-04-24 16:29:59 +08:00
committed by GitHub
parent a3d05e8987
commit 03c2862404
2 changed files with 77 additions and 8 deletions

View File

@@ -26,7 +26,9 @@
#include <__ranges/size.h>
#include <__ranges/transform_view.h>
#include <__type_traits/add_pointer.h>
#include <__type_traits/is_class.h>
#include <__type_traits/is_const.h>
#include <__type_traits/is_union.h>
#include <__type_traits/is_volatile.h>
#include <__type_traits/type_identity.h>
#include <__utility/declval.h>
@@ -81,7 +83,7 @@ template <class _Container, input_range _Range, class... _Args>
static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
static_assert(
!is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
static_assert(is_class_v<_Container> || is_union_v<_Container>, "The target must be a class type or union type");
// First see if the non-recursive case applies -- the conversion target is either:
// - a range with a convertible value type;
// - a non-range type which might support being created from the input argument(s) (e.g. an `optional`).
@@ -208,7 +210,7 @@ template <class _Container, class... _Args>
static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
static_assert(
!is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
static_assert(is_class_v<_Container> || is_union_v<_Container>, "The target must be a class type or union type");
auto __to_func = []<input_range _Range, class... _Tail>(_Range&& __range, _Tail&&... __tail) static
requires requires { //
/**/ ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);

View File

@@ -14,12 +14,79 @@
#include <ranges>
#include <vector>
void test() {
void test_cv_qualifications() {
using R = std::vector<int>;
R in = {1, 2, 3};
R in = {1, 2, 3};
(void)std::ranges::to<const R>(in); //expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be const-qualified, please remove the const}}
(void)(in | std::ranges::to<const R>()); //expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be const-qualified, please remove the const}}
(void)std::ranges::to<volatile R>(in); //expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
(void)(in | std::ranges::to<volatile R>()); //expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
//expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be const-qualified, please remove the const}}
(void)std::ranges::to<const R>(in);
//expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be const-qualified, please remove the const}}
(void)(in | std::ranges::to<const R>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
(void)std::ranges::to<volatile R>(in);
//expected-error-re@*:* {{static assertion failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
(void)(in | std::ranges::to<volatile R>());
}
//unexpected_types
void ff();
void test_unexpected_types() {
struct C {
int member;
int f();
};
enum color { red, green, blue };
using member_func_ptr = decltype(&C::f);
using member_ptr = decltype(&C::member);
using func_ptr = decltype(&ff);
using func_t = decltype(ff);
struct R {
int* begin() const { return nullptr; };
int* end() const { return nullptr; };
operator int() const;
operator int*() const;
operator func_ptr() const;
operator member_func_ptr() const;
operator member_ptr() const;
operator color() const;
};
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<int>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)(R{} | std::ranges::to<int>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<int*>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)(R{} | std::ranges::to<int*>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<func_ptr>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)(R{} | std::ranges::to<func_ptr>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<member_ptr>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)(R{} | std::ranges::to<member_ptr>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<func_t>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)(R{} | std::ranges::to<func_t>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<void>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
//expected-error-re@*:* {{static assertion failed{{.*}}ranges::to: unable to convert to the given container type.}}
(void)(R{} | std::ranges::to<void>());
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)std::ranges::to<color>(R{});
//expected-error-re@*:* {{static assertion failed{{.*}}The target must be a class type}}
(void)(R{} | std::ranges::to<color>());
}