From: Louis Dionne Date: Thu, 5 May 2022 16:24:43 +0000 (-0400) Subject: [libc++] Avoid creating temporaries in unary expressions involving valarray X-Git-Tag: upstream/15.0.7~5769 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b8f6f9e741c60a06cbe14adf890db0889b3b333f;p=platform%2Fupstream%2Fllvm.git [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 --- diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index f6bca74..c2a43a6 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 05ba8cb..6f6af65 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 operator!() const; + _LIBCPP_INLINE_VISIBILITY + __val_expr<_UnaryOp<__unary_plus<_Tp>, const valarray&> > operator+() const; + _LIBCPP_INLINE_VISIBILITY + __val_expr<_UnaryOp, const valarray&> > operator-() const; + _LIBCPP_INLINE_VISIBILITY + __val_expr<_UnaryOp<__bit_not<_Tp>, const valarray&> > operator~() const; + _LIBCPP_INLINE_VISIBILITY + __val_expr<_UnaryOp, const valarray&> > operator!() const; // computed assignment: _LIBCPP_INLINE_VISIBILITY @@ -3300,63 +3304,39 @@ valarray<_Tp>::operator[](valarray&& __vs) #endif // _LIBCPP_CXX03_LANG template -valarray<_Tp> +inline +__val_expr<_UnaryOp<__unary_plus<_Tp>, const valarray<_Tp>&> > valarray<_Tp>::operator+() const { - valarray __r; - size_t __n = size(); - if (__n) - { - __r.__begin_ = __r.__end_ = allocator().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 -valarray<_Tp> +inline +__val_expr<_UnaryOp, const valarray<_Tp>&> > valarray<_Tp>::operator-() const { - valarray __r; - size_t __n = size(); - if (__n) - { - __r.__begin_ = __r.__end_ = allocator().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, const valarray<_Tp>&>; + return __val_expr<_Op>(_Op(negate<_Tp>(), *this)); } template -valarray<_Tp> +inline +__val_expr<_UnaryOp<__bit_not<_Tp>, const valarray<_Tp>&> > valarray<_Tp>::operator~() const { - valarray __r; - size_t __n = size(); - if (__n) - { - __r.__begin_ = __r.__end_ = allocator().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 -valarray +inline +__val_expr<_UnaryOp, const valarray<_Tp>&> > valarray<_Tp>::operator!() const { - valarray __r; - size_t __n = size(); - if (__n) - { - __r.__begin_ = __r.__end_ = allocator().allocate(__n); - for (const value_type* __p = __begin_; __n; ++__r.__end_, ++__p, --__n) - ::new ((void*)__r.__end_) bool(!*__p); - } - return __r; + using _Op = _UnaryOp, const valarray<_Tp>&>; + return __val_expr<_Op>(_Op(logical_not<_Tp>(), *this)); } template 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 eac7a7a..e59b571 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 a(array, 3); + std::valarray 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 5bcf4b1..7f8defc 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 a(array, 3); + std::valarray 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 7144026..58653fc 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 a(array, 3); + std::valarray 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 4193efe..be5540e 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 a(array, 3); + std::valarray 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; }