forked from OSchip/llvm-project
[libc++] Avoid creating temporaries in unary expressions involving valarray
Currently, unary expressions involving valarray will create a temporary. This leads to dangling references in expressions like `-a * b`, because `-a` is a temporary and the resulting expression will refer to it. This patch fixes the problem by creating a lazy expression to perform the unary operation instead of eagerly creating a temporary valarray. This is permitted by the Standard, which does not specify the exact type of most expressions involving valarrays. This is technically an ABI break, however I believe the actual potential for breakage is very low. rdar://90152242 Differential Revision: https://reviews.llvm.org/D125019
This commit is contained in:
parent
04d4130a51
commit
b8f6f9e741
|
@ -122,6 +122,17 @@ ABI Changes
|
|||
token used when constructing a ``std::random_device`` will now be ignored instead of
|
||||
interpreted as a file to read entropy from.
|
||||
|
||||
- ``std::valarray``'s unary operators ``!``, ``+``, ``~`` and ``-`` now return an expression
|
||||
object instead of a ``valarray``. This was done to fix an issue where any expression involving
|
||||
other ``valarray`` operators and one of these unary operators would end up with a dangling
|
||||
reference. This is a potential ABI break for code that exposes ``std::valarray`` on an ABI
|
||||
boundary, specifically if the return type of an ABI-boundary function is ``auto``-deduced
|
||||
from an expression involving unary operators on ``valarray``. If you are concerned by this,
|
||||
you can audit whether your executable or library exports any function that returns a
|
||||
``valarray``, and if so ensure that any such function uses ``std::valarray`` directly
|
||||
as a return type instead of relying on the type of ``valarray``-expressions, which is
|
||||
not guaranteed by the Standard anyway.
|
||||
|
||||
Build System Changes
|
||||
--------------------
|
||||
|
||||
|
|
|
@ -923,10 +923,14 @@ public:
|
|||
#endif // _LIBCPP_CXX03_LANG
|
||||
|
||||
// unary operators:
|
||||
valarray operator+() const;
|
||||
valarray operator-() const;
|
||||
valarray operator~() const;
|
||||
valarray<bool> operator!() const;
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__val_expr<_UnaryOp<__unary_plus<_Tp>, const valarray&> > operator+() const;
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__val_expr<_UnaryOp<negate<_Tp>, const valarray&> > operator-() const;
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__val_expr<_UnaryOp<__bit_not<_Tp>, const valarray&> > operator~() const;
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
__val_expr<_UnaryOp<logical_not<_Tp>, const valarray&> > operator!() const;
|
||||
|
||||
// computed assignment:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
|
@ -3300,63 +3304,39 @@ valarray<_Tp>::operator[](valarray<size_t>&& __vs)
|
|||
#endif // _LIBCPP_CXX03_LANG
|
||||
|
||||
template <class _Tp>
|
||||
valarray<_Tp>
|
||||
inline
|
||||
__val_expr<_UnaryOp<__unary_plus<_Tp>, const valarray<_Tp>&> >
|
||||
valarray<_Tp>::operator+() const
|
||||
{
|
||||
valarray<value_type> __r;
|
||||
size_t __n = size();
|
||||
if (__n)
|
||||
{
|
||||
__r.__begin_ = __r.__end_ = allocator<value_type>().allocate(__n);
|
||||
for (const value_type* __p = __begin_; __n; ++__r.__end_, ++__p, --__n)
|
||||
::new ((void*)__r.__end_) value_type(+*__p);
|
||||
}
|
||||
return __r;
|
||||
using _Op = _UnaryOp<__unary_plus<_Tp>, const valarray<_Tp>&>;
|
||||
return __val_expr<_Op>(_Op(__unary_plus<_Tp>(), *this));
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
valarray<_Tp>
|
||||
inline
|
||||
__val_expr<_UnaryOp<negate<_Tp>, const valarray<_Tp>&> >
|
||||
valarray<_Tp>::operator-() const
|
||||
{
|
||||
valarray<value_type> __r;
|
||||
size_t __n = size();
|
||||
if (__n)
|
||||
{
|
||||
__r.__begin_ = __r.__end_ = allocator<value_type>().allocate(__n);
|
||||
for (const value_type* __p = __begin_; __n; ++__r.__end_, ++__p, --__n)
|
||||
::new ((void*)__r.__end_) value_type(-*__p);
|
||||
}
|
||||
return __r;
|
||||
using _Op = _UnaryOp<negate<_Tp>, const valarray<_Tp>&>;
|
||||
return __val_expr<_Op>(_Op(negate<_Tp>(), *this));
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
valarray<_Tp>
|
||||
inline
|
||||
__val_expr<_UnaryOp<__bit_not<_Tp>, const valarray<_Tp>&> >
|
||||
valarray<_Tp>::operator~() const
|
||||
{
|
||||
valarray<value_type> __r;
|
||||
size_t __n = size();
|
||||
if (__n)
|
||||
{
|
||||
__r.__begin_ = __r.__end_ = allocator<value_type>().allocate(__n);
|
||||
for (const value_type* __p = __begin_; __n; ++__r.__end_, ++__p, --__n)
|
||||
::new ((void*)__r.__end_) value_type(~*__p);
|
||||
}
|
||||
return __r;
|
||||
using _Op = _UnaryOp<__bit_not<_Tp>, const valarray<_Tp>&>;
|
||||
return __val_expr<_Op>(_Op(__bit_not<_Tp>(), *this));
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
valarray<bool>
|
||||
inline
|
||||
__val_expr<_UnaryOp<logical_not<_Tp>, const valarray<_Tp>&> >
|
||||
valarray<_Tp>::operator!() const
|
||||
{
|
||||
valarray<bool> __r;
|
||||
size_t __n = size();
|
||||
if (__n)
|
||||
{
|
||||
__r.__begin_ = __r.__end_ = allocator<bool>().allocate(__n);
|
||||
for (const value_type* __p = __begin_; __n; ++__r.__end_, ++__p, --__n)
|
||||
::new ((void*)__r.__end_) bool(!*__p);
|
||||
}
|
||||
return __r;
|
||||
using _Op = _UnaryOp<logical_not<_Tp>, const valarray<_Tp>&>;
|
||||
return __val_expr<_Op>(_Op(logical_not<_Tp>(), *this));
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
|
|
|
@ -54,6 +54,15 @@ int main(int, char**)
|
|||
for (std::size_t i = 0; i < v2.size(); ++i)
|
||||
assert(v2[i] == ~(2*v[i]));
|
||||
}
|
||||
{
|
||||
// Make sure we don't have dangling reference problems with unary expressions
|
||||
int array[] = {1, 2, 3};
|
||||
std::valarray<int> a(array, 3);
|
||||
std::valarray<int> b(array, 3);
|
||||
auto c = ~a + b;
|
||||
assert(c.size() == 3);
|
||||
assert(c[0] == (~1 + 1) && c[1] == (~2 + 2) && c[2] == (~3 + 3));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,15 @@ int main(int, char**)
|
|||
for (std::size_t i = 0; i < v2.size(); ++i)
|
||||
assert(v2[i] == -2*v[i]);
|
||||
}
|
||||
{
|
||||
// Make sure we don't have dangling reference problems with unary expressions
|
||||
int array[] = {1, 2, 3};
|
||||
std::valarray<int> a(array, 3);
|
||||
std::valarray<int> b(array, 3);
|
||||
auto c = -a * b;
|
||||
assert(c.size() == 3);
|
||||
assert(c[0] == -1 && c[1] == -4 && c[2] == -9);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,15 @@ int main(int, char**)
|
|||
for (std::size_t i = 0; i < v2.size(); ++i)
|
||||
assert(v2[i] == !(v[i] + v[i]));
|
||||
}
|
||||
{
|
||||
// Make sure we don't have dangling reference problems with unary expressions
|
||||
bool array[] = {true, false, true};
|
||||
std::valarray<bool> a(array, 3);
|
||||
std::valarray<bool> b(array, 3);
|
||||
auto c = !a && b;
|
||||
assert(c.size() == 3);
|
||||
assert(c[0] == false && c[1] == false && c[2] == false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,15 @@ int main(int, char**)
|
|||
for (std::size_t i = 0; i < v2.size(); ++i)
|
||||
assert(v2[i] == +2*v[i]);
|
||||
}
|
||||
{
|
||||
// Make sure we don't have dangling reference problems with unary expressions
|
||||
int array[] = {1, 2, 3};
|
||||
std::valarray<int> a(array, 3);
|
||||
std::valarray<int> b(array, 3);
|
||||
auto c = +a * b;
|
||||
assert(c.size() == 3);
|
||||
assert(c[0] == 1 && c[1] == 4 && c[2] == 9);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue