[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

NOKEYCHECK=True
GitOrigin-RevId: b8f6f9e741c60a06cbe14adf890db0889b3b333f
diff --git a/include/valarray b/include/valarray
index 05ba8cb..6f6af65 100644
--- a/include/valarray
+++ b/include/valarray
@@ -923,10 +923,14 @@
 #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 @@
 #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>