[libcxx-commits] [libcxx] b8f6f9e - [libc++] Avoid creating temporaries in unary expressions involving valarray

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jun 6 09:59:04 PDT 2022


Author: Louis Dionne
Date: 2022-06-06T12:58:23-04:00
New Revision: b8f6f9e741c60a06cbe14adf890db0889b3b333f

URL: https://github.com/llvm/llvm-project/commit/b8f6f9e741c60a06cbe14adf890db0889b3b333f
DIFF: https://github.com/llvm/llvm-project/commit/b8f6f9e741c60a06cbe14adf890db0889b3b333f.diff

LOG: [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

Added: 
    

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/include/valarray
    libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/bit_not.pass.cpp
    libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/negate.pass.cpp
    libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/not.pass.cpp
    libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/plus.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index f6bca74f76a22..c2a43a6873ecb 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -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
 --------------------
 

diff  --git a/libcxx/include/valarray b/libcxx/include/valarray
index 05ba8cba37848..6f6af6571417c 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -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>

diff  --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/bit_not.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/bit_not.pass.cpp
index eac7a7afa48db..e59b571510793 100644
--- a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/bit_not.pass.cpp
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/bit_not.pass.cpp
@@ -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;
 }

diff  --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/negate.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/negate.pass.cpp
index 5bcf4b1adb4bd..7f8defc1b12d3 100644
--- a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/negate.pass.cpp
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/negate.pass.cpp
@@ -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;
 }

diff  --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/not.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/not.pass.cpp
index 714402602c487..58653fce6d169 100644
--- a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/not.pass.cpp
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/not.pass.cpp
@@ -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;
 }

diff  --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/plus.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/plus.pass.cpp
index 4193efe6e3b04..be5540e7162f4 100644
--- a/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/plus.pass.cpp
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/plus.pass.cpp
@@ -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;
 }


        


More information about the libcxx-commits mailing list