From patchwork Fri Apr 6 08:29:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 895635 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-475964-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="JSS9XCIn"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40HXt66G6hz9s1p for ; Fri, 6 Apr 2018 18:29:17 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:cc:content-type; q=dns; s=default; b=GPCUAdeWwMB1sxwtInobbdmjCSEW2MyG+aRxxE0FOtV FYGqPTag2y7F9DD+nZx2UId6UDr+R2m615++E54BlkOlzinZGHUGCZ6PbD5WThqY tJkDT3OQK2o/miS90wm5KnK6Lz4h0PEuV1zFTdE/eOEV9uYzyiEYRuXLOIhDv+rs = DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:cc:content-type; s=default; bh=Ibhp/ViT2KspmcTuBWgq4gMKGoU=; b=JSS9XCIn7laToZBWS wmzlCF/r3SKKd5xrAl6gapDdLGghSEL1gSCeYmNEVcgn8JbNOLw4O8F+3NuaQs8I fun7E/CfCg7aRHiS/a+bEvfIjm6EhVGH4LO7WysS1ISnGGN4XDnrOaD85wGBgaXV MCMewM0Kounv/wNmelWfF1YXHk= Received: (qmail 85498 invoked by alias); 6 Apr 2018 08:29:10 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 85479 invoked by uid 89); 6 Apr 2018 08:29:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.5 required=5.0 tests=AWL, BAYES_00, FILL_THIS_FORM, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=VERIFY, 1288, expired, sees X-Spam-User: qpsmtpd, 2 recipients X-HELO: mail-io0-f181.google.com Received: from mail-io0-f181.google.com (HELO mail-io0-f181.google.com) (209.85.223.181) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 06 Apr 2018 08:29:06 +0000 Received: by mail-io0-f181.google.com with SMTP id v13so785351iob.6; Fri, 06 Apr 2018 01:29:06 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to:cc; bh=ntUEsiZO8IF68N8yIxt5N2qW4SpJI5yOewHt6eF9k8Y=; b=H6VknReiGfaehOzU7oLyuEq9OV3PUGwVHfBcZTgweGJc5R2zUeHoLPL99wo8HznglV awhVUIZlNLP73cSHDCqGP9drtV8Lr9DcIvzkyhHtk+SrOCx7BwQJ062sZ1Wpwv19cdjV n+1PZPj1Gd5kqhQnvHWNXPEmW8TcFd5BHnwDb8g1500+dWRE4k7mjFLMPvXaH72SVPWL AzMCYb4Gw/r82J83d7K6wX02xiovkh80D/ggfjmjQ+oV7hd6i8Z80VxMKnUlexmt25xl t4uXxu7RVNKWfrWBS7cdV+5xOmhLXDfV87zC6rbp8EkuYMhQM+x0xYNfi8t/BwHjd+n0 MybA== X-Gm-Message-State: AElRT7HvMlASXeaz4NbQvNP1EWxXtH9RQQW30gQokncmIBeoDNJ5xvoR Ns1WNYhDXMy4ZLFihHwkdIDDQYNdpDl5u3tVN79MCl9t X-Google-Smtp-Source: AIpwx4+0g1IPidqwbiiFPlfUCENorbsOWgV2nwbjUJBn78vKxBizgzP7ObsDrBvMJDc7aOHuJmegXR+P1pgnRLcJjdE= X-Received: by 10.107.26.202 with SMTP id a193mr24381762ioa.120.1523003344206; Fri, 06 Apr 2018 01:29:04 -0700 (PDT) MIME-Version: 1.0 Received: by 10.107.30.10 with HTTP; Fri, 6 Apr 2018 01:29:03 -0700 (PDT) From: Jonathan Wakely Date: Fri, 6 Apr 2018 09:29:03 +0100 Message-ID: Subject: PR libstdc++/83860 avoid dangling references in valarray closure types To: "libstdc++" , gcc-patches Cc: Marc Glisse This attempts to solve some of the problems when mixing std::valarray operations and 'auto', by storing nested closure objects as values instead of references. This means we don't end up with dangling references to temporary closures that have already been destroyed. This makes the closure objects introduced by the library less error-prone, but it's still possible to write incorrect code by using temporary valarrays, e.g. std::valarray f(); auto x = f() * 2; std::valarray y = x; Here the closure object 'x' has a dangling reference to the temporary returned by f(). It might be possible to do something about this by overloading the operators for valarray rvalues and moving the valarray into the closure, instead of holding a const-reference. I don't plan to work on that. Performance seems to be unaffected by this patch, although I didn't test that very thoroughly. Strictly speaking this is an ABI change as it changes the size and layout of the closure types like _BinClos etc. so that their _M_expr member is at least two words, not just one for a reference. Those closure types should never appear in API boundaries or as class members (if anybody was doing that by using 'auto' it wouldn't have worked correctly anyway) so I think we can just change them, without renaming the types or moving them into an inline namespace so they mangle differently. Does anybody disagree? The PR is marked as a regression because the example in the PR used to "work" with older releases. That's probably only because they didn't optimize as aggressively and so the stack space of the expired temporaries wasn't reused (ASan still shows the bug was there in older releases even if it doesn't crash). As a regression this should be backported, but the layout changes make me pause a little when considering making the change on stable release branches. PR libstdc++/83860 * include/bits/gslice_array.h (gslice_array): Define default constructor as deleted, as per C++11 standard. * include/bits/mask_array.h (mask_array): Likewise. * include/bits/slice_array.h (slice_array): Likewise. * include/bits/valarray_after.h (_GBase::_M_expr, _IBase::_M_expr): Use _ValArrayRef for type of data members. * include/bits/valarray_before.h (_ValArrayRef): New helper for type of data members in closure objects. (_FunBase::_M_expr, _UnBase::_M_expr, _BinBase::_M_expr1) (_BinBase::_M_expr2, _BinBase2::_M_expr1, _BinBase1::_M_expr2) (_SBase::_M_expr): Use _ValArrayRef for type of data members. * testsuite/26_numerics/valarray/83860.cc: New. I'll commit this to trunk only for now, unless anybody sees a problem with the approach, or thinks the layout changes require new mangled names for the closures. commit 1dd9f0f54457eb2c44c6b1125800d94eaac0cb2f Author: Jonathan Wakely Date: Fri Apr 6 01:30:19 2018 +0100 PR libstdc++/83860 avoid dangling references in valarray closure types PR libstdc++/83860 * include/bits/gslice_array.h (gslice_array): Define default constructor as deleted, as per C++11 standard. * include/bits/mask_array.h (mask_array): Likewise. * include/bits/slice_array.h (slice_array): Likewise. * include/bits/valarray_after.h (_GBase::_M_expr, _IBase::_M_expr): Use _ValArrayRef for type of data members. * include/bits/valarray_before.h (_ValArrayRef): New helper for type of data members in closure objects. (_FunBase::_M_expr, _UnBase::_M_expr, _BinBase::_M_expr1) (_BinBase::_M_expr2, _BinBase2::_M_expr1, _BinBase1::_M_expr2) (_SBase::_M_expr): Use _ValArrayRef for type of data members. * testsuite/26_numerics/valarray/83860.cc: New. diff --git a/libstdc++-v3/include/bits/gslice_array.h b/libstdc++-v3/include/bits/gslice_array.h index 2da7e0442bb..715c53bd616 100644 --- a/libstdc++-v3/include/bits/gslice_array.h +++ b/libstdc++-v3/include/bits/gslice_array.h @@ -128,8 +128,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION gslice_array(_Array<_Tp>, const valarray&); +#if __cplusplus < 201103L // not implemented gslice_array(); +#else + public: + gslice_array() = delete; +#endif }; template diff --git a/libstdc++-v3/include/bits/mask_array.h b/libstdc++-v3/include/bits/mask_array.h index 84671cb43d6..c11691a243a 100644 --- a/libstdc++-v3/include/bits/mask_array.h +++ b/libstdc++-v3/include/bits/mask_array.h @@ -131,8 +131,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const _Array _M_mask; const _Array<_Tp> _M_array; +#if __cplusplus < 201103L // not implemented mask_array(); +#else + public: + mask_array() = delete; +#endif }; template diff --git a/libstdc++-v3/include/bits/slice_array.h b/libstdc++-v3/include/bits/slice_array.h index 05b096bb5a9..b025373180f 100644 --- a/libstdc++-v3/include/bits/slice_array.h +++ b/libstdc++-v3/include/bits/slice_array.h @@ -192,8 +192,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const size_t _M_stride; const _Array<_Tp> _M_array; +#if __cplusplus < 201103L // not implemented slice_array(); +#else + public: + slice_array() = delete; +#endif }; template diff --git a/libstdc++-v3/include/bits/valarray_after.h b/libstdc++-v3/include/bits/valarray_after.h index 7f62b292ed5..3cfd6a510d7 100644 --- a/libstdc++-v3/include/bits/valarray_after.h +++ b/libstdc++-v3/include/bits/valarray_after.h @@ -59,8 +59,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_index.size(); } private: - const _Dom& _M_expr; - const valarray& _M_index; + typename _ValArrayRef<_Dom>::__type _M_expr; + const valarray& _M_index; }; template @@ -128,8 +128,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_index.size(); } private: - const _Dom& _M_expr; - const valarray& _M_index; + typename _ValArrayRef<_Dom>::__type _M_expr; + const valarray& _M_index; }; template diff --git a/libstdc++-v3/include/bits/valarray_before.h b/libstdc++-v3/include/bits/valarray_before.h index a77fdf203b2..73f0b4f61e5 100644 --- a/libstdc++-v3/include/bits/valarray_before.h +++ b/libstdc++-v3/include/bits/valarray_before.h @@ -406,6 +406,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef bool result_type; }; + // Closure types already have reference semantics and are often short-lived, + // so store them by value to avoid (some cases of) dangling references to + // out-of-scope temporaries. + template + struct _ValArrayRef + { typedef const _Tp __type; }; + + // Use real references for std::valarray objects. + template + struct _ValArrayRef< valarray<_Tp> > + { typedef const valarray<_Tp>& __type; }; + // // Apply function taking a value/const reference closure // @@ -425,7 +437,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t size() const { return _M_expr.size ();} private: - const _Dom& _M_expr; + typename _ValArrayRef<_Dom>::__type _M_expr; value_type (*_M_func)(_Arg); }; @@ -490,7 +502,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t size() const { return _M_expr.size(); } private: - const _Arg& _M_expr; + typename _ValArrayRef<_Arg>::__type _M_expr; }; template @@ -536,8 +548,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t size() const { return _M_expr1.size(); } private: - const _FirstArg& _M_expr1; - const _SecondArg& _M_expr2; + typename _ValArrayRef<_FirstArg>::__type _M_expr1; + typename _ValArrayRef<_SecondArg>::__type _M_expr2; }; @@ -557,8 +569,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t size() const { return _M_expr1.size(); } private: - const _Clos& _M_expr1; - const _Vt& _M_expr2; + typename _ValArrayRef<_Clos>::__type _M_expr1; + _Vt _M_expr2; }; template @@ -577,8 +589,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION size_t size() const { return _M_expr2.size(); } private: - const _Vt& _M_expr1; - const _Clos& _M_expr2; + _Vt _M_expr1; + typename _ValArrayRef<_Clos>::__type _M_expr2; }; template @@ -592,7 +604,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; template - struct _BinClos<_Oper,_ValArray, _ValArray, _Tp, _Tp> + struct _BinClos<_Oper, _ValArray, _ValArray, _Tp, _Tp> : _BinBase<_Oper, valarray<_Tp>, valarray<_Tp> > { typedef _BinBase<_Oper, valarray<_Tp>, valarray<_Tp> > _Base; @@ -671,7 +683,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // // slice_array closure. // - template + template class _SBase { public: @@ -689,7 +701,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _M_slice.size (); } private: - const _Dom& _M_expr; + typename _ValArrayRef<_Dom>::__type _M_expr; const slice& _M_slice; }; diff --git a/libstdc++-v3/testsuite/26_numerics/valarray/83860.cc b/libstdc++-v3/testsuite/26_numerics/valarray/83860.cc new file mode 100644 index 00000000000..a9599798b41 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/valarray/83860.cc @@ -0,0 +1,105 @@ +// Copyright (C) 2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run { target c++11 } } + +#include +#include + +const std::valarray v{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +bool +all_of(const std::valarray& vals) +{ + for (bool b : vals) + if (!b) + return false; + return true; +} + +void +test01() +{ + // PR libstdc++/83860 + auto sum = v + v + v; + std::valarray vsum = sum; + VERIFY( all_of( vsum == (v + v + v) ) ); +} + +void +test02() +{ + auto neg = -(-v); + std::valarray vneg = neg; + VERIFY( all_of( vneg == v ) ); +} + +void +test03() +{ + auto diff = v + -v; + std::valarray vdiff = diff; + VERIFY( all_of( vdiff == (v - v) ) ); +} + +void +test04() +{ + auto sum = -v + -v; + std::valarray vsum = sum; + VERIFY( all_of( vsum == (-v + -v) ) ); +} + +void +test05() +{ + auto sum = -(-v + -v); + std::valarray vsum = sum; + VERIFY( all_of( vsum == (v + v) ) ); +} + +void +test06() +{ + auto prod = 3 * +v * 2; + std::valarray vprod = prod; + VERIFY( all_of( vprod == (6 * v) ) ); +} + +void +test07() +{ + auto valfun = [](int i) { return i; }; + auto reffun = [](const int& i) { return i; }; + auto sum = (v.apply(valfun) + v.apply(reffun)); + std::valarray vsum = sum; + VERIFY( all_of( vsum == (v + v) ) ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); +}