Skip to content

Commit 5ca2777

Browse files
authored
[libc++] Fixes valarray proxy type compound assignment operations. (#76528)
The valarray<>::operator[](...) const functions return proxy objects. The valarray<>::operator[](...) functions return valarray objects. However the standard allows functions returning valarray objects to return custom proxy objects instead. Libc++ returns __val_expr proxies. Functions taking a valarray object must work with the custom proxies too. Therefore several operations have a custom proxy overload instead of valarray overloads. Libc++ doesn't specify a valarray overload. This is an issue with the standard proxy types; these can implicitly be converted to a valarray. The solution is to allow the standard proxies to behave as-if they are custom proxies. This patch fixes the valarray compound assignments. Other operations, like the binary non-member functions are not fixed. These will be done in a followup patch. Fixes: #21320
1 parent 47abbf4 commit 5ca2777

File tree

19 files changed

+1062
-180
lines changed

19 files changed

+1062
-180
lines changed

libcxx/include/valarray

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,50 @@ struct __is_val_expr<__val_expr<_ValExpr> > : true_type {};
733733
template <class _Tp>
734734
struct __is_val_expr<valarray<_Tp> > : true_type {};
735735

736+
template <class _Tp>
737+
struct __is_val_expr<slice_array<_Tp> > : true_type {};
738+
739+
template <class _Tp>
740+
struct __is_val_expr<gslice_array<_Tp> > : true_type {};
741+
742+
template <class _Tp>
743+
struct __is_val_expr<mask_array<_Tp> > : true_type {};
744+
745+
template <class _Tp>
746+
struct __is_val_expr<indirect_array<_Tp> > : true_type {};
747+
748+
// The functions using a __val_expr access the elements by their index.
749+
// valarray and the libc++ lazy proxies have an operator[]. The
750+
// Standard proxy array's don't have this operator, instead they have a
751+
// implementation specific accessor
752+
// __get(size_t)
753+
//
754+
// The functions use the non-member function
755+
// __get(__val_expr, size_t)
756+
//
757+
// If the __val_expr is a specialization of __val_expr_use_member_functions it
758+
// uses the __val_expr's member function
759+
// __get(size_t)
760+
// else it uses the __val_expr's member function
761+
// operator[](size_t)
762+
template <class _ValExpr>
763+
struct __val_expr_use_member_functions;
764+
765+
template <class>
766+
struct __val_expr_use_member_functions : false_type {};
767+
768+
template <class _Tp>
769+
struct __val_expr_use_member_functions<slice_array<_Tp> > : true_type {};
770+
771+
template <class _Tp>
772+
struct __val_expr_use_member_functions<gslice_array<_Tp> > : true_type {};
773+
774+
template <class _Tp>
775+
struct __val_expr_use_member_functions<mask_array<_Tp> > : true_type {};
776+
777+
template <class _Tp>
778+
struct __val_expr_use_member_functions<indirect_array<_Tp> > : true_type {};
779+
736780
template <class _Tp>
737781
class _LIBCPP_TEMPLATE_VIS valarray {
738782
public:
@@ -903,6 +947,18 @@ template <class _Tp, size_t _Size>
903947
valarray(const _Tp (&)[_Size], size_t) -> valarray<_Tp>;
904948
#endif
905949

950+
template <class _Expr,
951+
__enable_if_t<__is_val_expr<_Expr>::value && __val_expr_use_member_functions<_Expr>::value, int> = 0>
952+
_LIBCPP_HIDE_FROM_ABI typename _Expr::value_type __get(const _Expr& __v, size_t __i) {
953+
return __v.__get(__i);
954+
}
955+
956+
template <class _Expr,
957+
__enable_if_t<__is_val_expr<_Expr>::value && !__val_expr_use_member_functions<_Expr>::value, int> = 0>
958+
_LIBCPP_HIDE_FROM_ABI typename _Expr::value_type __get(const _Expr& __v, size_t __i) {
959+
return __v[__i];
960+
}
961+
906962
extern template _LIBCPP_EXPORTED_FROM_ABI void valarray<size_t>::resize(size_t, size_t);
907963

908964
template <class _Op, class _Tp>
@@ -1025,6 +1081,12 @@ public:
10251081

10261082
_LIBCPP_HIDE_FROM_ABI void operator=(const valarray<value_type>& __va) const;
10271083

1084+
// Behaves like __val_expr::operator[], which returns by value.
1085+
_LIBCPP_HIDE_FROM_ABI value_type __get(size_t __i) const {
1086+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "slice_array.__get() index out of bounds");
1087+
return __vp_[__i * __stride_];
1088+
}
1089+
10281090
private:
10291091
_LIBCPP_HIDE_FROM_ABI slice_array(const slice& __sl, const valarray<value_type>& __v)
10301092
: __vp_(const_cast<value_type*>(__v.__begin_ + __sl.start())), __size_(__sl.size()), __stride_(__sl.stride()) {}
@@ -1246,6 +1308,12 @@ public:
12461308

12471309
gslice_array(const gslice_array&) = default;
12481310

1311+
// Behaves like __val_expr::operator[], which returns by value.
1312+
_LIBCPP_HIDE_FROM_ABI value_type __get(size_t __i) const {
1313+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __1d_.size(), "gslice_array.__get() index out of bounds");
1314+
return __vp_[__1d_[__i]];
1315+
}
1316+
12491317
private:
12501318
gslice_array(const gslice& __gs, const valarray<value_type>& __v)
12511319
: __vp_(const_cast<value_type*>(__v.__begin_)), __1d_(__gs.__1d_) {}
@@ -1425,6 +1493,12 @@ public:
14251493

14261494
_LIBCPP_HIDE_FROM_ABI void operator=(const value_type& __x) const;
14271495

1496+
// Behaves like __val_expr::operator[], which returns by value.
1497+
_LIBCPP_HIDE_FROM_ABI value_type __get(size_t __i) const {
1498+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __1d_.size(), "mask_array.__get() index out of bounds");
1499+
return __vp_[__1d_[__i]];
1500+
}
1501+
14281502
private:
14291503
_LIBCPP_HIDE_FROM_ABI mask_array(const valarray<bool>& __vb, const valarray<value_type>& __v)
14301504
: __vp_(const_cast<value_type*>(__v.__begin_)),
@@ -1624,6 +1698,12 @@ public:
16241698

16251699
_LIBCPP_HIDE_FROM_ABI void operator=(const value_type& __x) const;
16261700

1701+
// Behaves like __val_expr::operator[], which returns by value.
1702+
_LIBCPP_HIDE_FROM_ABI value_type __get(size_t __i) const {
1703+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __1d_.size(), "indirect_array.__get() index out of bounds");
1704+
return __vp_[__1d_[__i]];
1705+
}
1706+
16271707
private:
16281708
_LIBCPP_HIDE_FROM_ABI indirect_array(const valarray<size_t>& __ia, const valarray<value_type>& __v)
16291709
: __vp_(const_cast<value_type*>(__v.__begin_)), __1d_(__ia) {}
@@ -2355,7 +2435,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
23552435
inline valarray<_Tp>& valarray<_Tp>::operator*=(const _Expr& __v) {
23562436
size_t __i = 0;
23572437
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2358-
*__t *= __v[__i];
2438+
*__t *= std::__get(__v,__i);
23592439
return *this;
23602440
}
23612441

@@ -2364,7 +2444,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
23642444
inline valarray<_Tp>& valarray<_Tp>::operator/=(const _Expr& __v) {
23652445
size_t __i = 0;
23662446
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2367-
*__t /= __v[__i];
2447+
*__t /= std::__get(__v,__i);
23682448
return *this;
23692449
}
23702450

@@ -2373,7 +2453,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
23732453
inline valarray<_Tp>& valarray<_Tp>::operator%=(const _Expr& __v) {
23742454
size_t __i = 0;
23752455
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2376-
*__t %= __v[__i];
2456+
*__t %= std::__get(__v, __i);
23772457
return *this;
23782458
}
23792459

@@ -2382,7 +2462,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
23822462
inline valarray<_Tp>& valarray<_Tp>::operator+=(const _Expr& __v) {
23832463
size_t __i = 0;
23842464
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2385-
*__t += __v[__i];
2465+
*__t += std::__get(__v, __i);
23862466
return *this;
23872467
}
23882468

@@ -2391,7 +2471,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
23912471
inline valarray<_Tp>& valarray<_Tp>::operator-=(const _Expr& __v) {
23922472
size_t __i = 0;
23932473
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2394-
*__t -= __v[__i];
2474+
*__t -= std::__get(__v, __i);
23952475
return *this;
23962476
}
23972477

@@ -2400,7 +2480,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
24002480
inline valarray<_Tp>& valarray<_Tp>::operator^=(const _Expr& __v) {
24012481
size_t __i = 0;
24022482
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2403-
*__t ^= __v[__i];
2483+
*__t ^= std::__get(__v, __i);
24042484
return *this;
24052485
}
24062486

@@ -2409,7 +2489,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
24092489
inline valarray<_Tp>& valarray<_Tp>::operator|=(const _Expr& __v) {
24102490
size_t __i = 0;
24112491
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2412-
*__t |= __v[__i];
2492+
*__t |= std::__get(__v, __i);
24132493
return *this;
24142494
}
24152495

@@ -2418,7 +2498,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
24182498
inline valarray<_Tp>& valarray<_Tp>::operator&=(const _Expr& __v) {
24192499
size_t __i = 0;
24202500
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2421-
*__t &= __v[__i];
2501+
*__t &= std::__get(__v, __i);
24222502
return *this;
24232503
}
24242504

@@ -2427,7 +2507,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
24272507
inline valarray<_Tp>& valarray<_Tp>::operator<<=(const _Expr& __v) {
24282508
size_t __i = 0;
24292509
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2430-
*__t <<= __v[__i];
2510+
*__t <<= std::__get(__v, __i);
24312511
return *this;
24322512
}
24332513

@@ -2436,7 +2516,7 @@ template <class _Expr, __enable_if_t<__is_val_expr<_Expr>::value, int> >
24362516
inline valarray<_Tp>& valarray<_Tp>::operator>>=(const _Expr& __v) {
24372517
size_t __i = 0;
24382518
for (value_type* __t = __begin_; __t != __end_; ++__t, ++__i)
2439-
*__t >>= __v[__i];
2519+
*__t >>= std::__get(__v, __i);
24402520
return *this;
24412521
}
24422522

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
// REQUIRES: has-unix-headers
10+
// UNSUPPORTED: c++03
11+
// UNSUPPORTED: libcpp-hardening-mode=none
12+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
13+
14+
// <valarray>
15+
16+
// template<class T> class gslice_array;
17+
18+
// T __get(size_t i); // where i is out of bounds
19+
20+
#include <valarray>
21+
22+
#include "check_assertion.h"
23+
24+
int main(int, char**) {
25+
unsigned input[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
26+
const unsigned N = sizeof(input) / sizeof(input[0]);
27+
28+
std::valarray<unsigned> array(input, N);
29+
30+
{
31+
std::gslice_array<unsigned> result =
32+
array[std::gslice(0, std::valarray<std::size_t>(N, 1), std::valarray<std::size_t>(1, 1))];
33+
TEST_LIBCPP_ASSERT_FAILURE(result.__get(N), "gslice_array.__get() index out of bounds");
34+
}
35+
{
36+
std::valarray<std::size_t> sizes(2);
37+
sizes[0] = 2;
38+
sizes[1] = 3;
39+
40+
std::valarray<std::size_t> strides(2);
41+
strides[0] = 6;
42+
strides[1] = 1;
43+
44+
std::gslice_array<unsigned> result = array[std::gslice(1, sizes, strides)];
45+
TEST_LIBCPP_ASSERT_FAILURE(result.__get(6), "gslice_array.__get() index out of bounds");
46+
}
47+
48+
return 0;
49+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
// <valarray>
10+
11+
// template<class T> class gslice_array;
12+
13+
// T __get(size_t i);
14+
15+
#include <valarray>
16+
#include <cassert>
17+
18+
#include "test_macros.h"
19+
20+
int main(int, char**) {
21+
unsigned input[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
22+
const unsigned N = sizeof(input) / sizeof(input[0]);
23+
24+
std::valarray<unsigned> array(input, N);
25+
26+
{
27+
std::gslice_array<unsigned> result =
28+
array[std::gslice(0, std::valarray<std::size_t>(N, 1), std::valarray<std::size_t>(1, 1))];
29+
for (unsigned i = 0; i < N; ++i)
30+
assert(result.__get(i) == i);
31+
}
32+
33+
{
34+
std::valarray<std::size_t> sizes(2);
35+
sizes[0] = 2;
36+
sizes[1] = 3;
37+
38+
std::valarray<std::size_t> strides(2);
39+
strides[0] = 6;
40+
strides[1] = 1;
41+
42+
std::gslice_array<unsigned> result = array[std::gslice(1, sizes, strides)];
43+
assert(result.__get(0) == input[1 + 0 * 6 + 0 * 1]);
44+
assert(result.__get(1) == input[1 + 0 * 6 + 1 * 1]);
45+
assert(result.__get(2) == input[1 + 0 * 6 + 2 * 1]);
46+
47+
assert(result.__get(3) == input[1 + 1 * 6 + 0 * 1]);
48+
assert(result.__get(4) == input[1 + 1 * 6 + 1 * 1]);
49+
assert(result.__get(5) == input[1 + 1 * 6 + 2 * 1]);
50+
}
51+
return 0;
52+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
// REQUIRES: has-unix-headers
10+
// UNSUPPORTED: c++03
11+
// UNSUPPORTED: libcpp-hardening-mode=none
12+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
13+
14+
// <valarray>
15+
16+
// template<class T> class indirect_array;
17+
18+
// T __get(size_t i); // where i is out of bounds
19+
20+
#include <valarray>
21+
22+
#include "check_assertion.h"
23+
24+
int main(int, char**) {
25+
unsigned input[] = {0, 1, 2, 3, 4};
26+
const unsigned N = sizeof(input) / sizeof(input[0]);
27+
28+
std::valarray<unsigned> array(input, N);
29+
30+
{
31+
std::indirect_array<unsigned> result = array[std::valarray<std::size_t>()];
32+
TEST_LIBCPP_ASSERT_FAILURE(result.__get(0), "indirect_array.__get() index out of bounds");
33+
}
34+
{
35+
std::indirect_array<unsigned> result = array[std::valarray<std::size_t>(std::size_t(0), std::size_t(N))];
36+
TEST_LIBCPP_ASSERT_FAILURE(result.__get(N), "indirect_array.__get() index out of bounds");
37+
}
38+
39+
{
40+
std::valarray<std::size_t> indirect(std::size_t(0), std::size_t(3));
41+
std::indirect_array<unsigned> result = array[indirect];
42+
indirect[0] = 4;
43+
indirect[1] = 1;
44+
indirect[2] = 3;
45+
TEST_LIBCPP_ASSERT_FAILURE(result.__get(3), "indirect_array.__get() index out of bounds");
46+
}
47+
48+
return 0;
49+
}

0 commit comments

Comments
 (0)