From patchwork Fri Mar 1 19:24:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 209057 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7301:fa17:b0:10a:f01:a869 with SMTP id ju23csp106441dyc; Fri, 1 Mar 2024 11:25:54 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCXrwDfOBLQbfxGw7I01RX67Dird6EreMf9w7nBvJTbQN7cBnN2sK7J0MC8o1eatrL5er9xbeOlZAbzd6da1DSjoMNEndw== X-Google-Smtp-Source: AGHT+IHCvnLel6CNWQ+XXQClwE9ezNHbgDLHGbhBmmcNaCE1zXYPWn99W2gA9ep9s3yseVVlfaKT X-Received: by 2002:ac8:5756:0:b0:42e:cc6f:da03 with SMTP id 22-20020ac85756000000b0042ecc6fda03mr2491292qtx.1.1709321154376; Fri, 01 Mar 2024 11:25:54 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1709321154; cv=pass; d=google.com; s=arc-20160816; b=rdEzmOq77Aww7juT7NTiG8KA5XkCr7A0hJDA2zF93tveaoPiOQpvGnz0LRBRuJSKjO Rc95JwSbXdTV0H6DCmYXxNODvlRk9OhQeorNUqNznYh7Au83mS0LJAtgev+WVpR38RJN 0pq5ht+NJU9HuRF9KtAATuP6FKuEBK9EGGcVkE3zz3ZL5NExuptRrwwHnkPOXC+SlPYj kYFHtEr4I2fF+Ekvgak4xetV67NR00uf+BezDF/LjrVvNtRbw4AGhLa1b7NeGCUTiQgp u9lOhuINvKlTUn2n5pwHXrAJLO+70KM34a+rdlzSwOnRHFhR//cktNf82qzZDKtgVh8w yzhA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-disposition:user-agent :in-reply-to:mime-version:references:message-id:subject:cc:to:from :date:dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=Dl0hG1gfpdXDNBj9QgzH3/3Rjape0SFJCzj0uqKWpeQ=; fh=rsfJLMIL/7j+58qzkgIXBlfQSah3iXypQc76SN5PqTs=; b=BBTeptoPkSqdW7NbDyhkfM/pKXTrEi/9bQ0bdAFkqtd2U27J+FPdZgHht+7IPyi3tm qBpYm4GzoR/mhV3VHnYJSM1sMP6GMcMD/p7W9GfcFkskzza0cp/9oSQz0OqItEI1c1zw GjHbuwz55biByxavnLkMs81RQ8p8kJo6d/qTNSy3WCtFTp3MTcxK3fOSUWeCZ+9KX4/P oz2pqEae8H/NRuHi16IKVBfpbKbYlbNql3TUxApRMtXjOy/yFVB0OiVIgOj5fkBEp+VG wA24z3cp/k3f2ms5qnNt+F9ADk6Tvn1qYRwdE434RZMXpvnWZ4OHGzLY567iJ9vnucCO 0TyQ==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=VMwagph1; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id f42-20020a05622a1a2a00b0042ece3b4ef8si1373030qtb.805.2024.03.01.11.25.54 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 Mar 2024 11:25:54 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=VMwagph1; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 131113858C31 for ; Fri, 1 Mar 2024 19:25:54 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id E75C33858C5F for ; Fri, 1 Mar 2024 19:24:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E75C33858C5F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E75C33858C5F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1709321091; cv=none; b=wUm+4SuDPUFmF8/W6tYWKXYidGpbsLL9ZoI9pnIUJ4GYFmrN6IrqJmyjc5TXAZ7mlwQAHU4bNspCpLVxPkCWFDsWLYBaxy3F4UevP6OPHejaWpzCgSMQddj5Gsyf+I4nsW6mCzja8W3df5Rmcam7RIMTzE5i/z0ZLyX+DqpuoIw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1709321091; c=relaxed/simple; bh=nDAEGc8x0QPL0ndXLngochojRfsIFbqLvkbr/sY3vz8=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=PfrAheAidaceQeGmN/m2if2TQDZF9Sfdf15JqNgOZatta+lcMQkIqwJ33rnbYDqjIFCkKDJk9VNE3iECJeKX2WwPEFVACZt37jt37BjqeiU0t9cOie6ftzxf9jUGmRI44IKXxu4FhQ1PHyXiVZtrwd2KMBwRwtG3J9olsoEoRi4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1709321087; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Dl0hG1gfpdXDNBj9QgzH3/3Rjape0SFJCzj0uqKWpeQ=; b=VMwagph1wpLvbwcyh+/DO+L2LYKTWNW+2ut8fKZMai2MKDBqnFMwMEy++qTgIWWlNRcx3m Oi3hBplSEXVVSsUn0W9+gB9X242Dc0RmSZTt/315amHIftDCVMqV8KsMDMQ/Gezmw5TMq0 IWzMgZq1pq6rKLjJWSJ3ANkDjaJ5hHw= Received: from mail-oo1-f72.google.com (mail-oo1-f72.google.com [209.85.161.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-682-yXc3G-BIM96NWtNjUY4Lgg-1; Fri, 01 Mar 2024 14:24:46 -0500 X-MC-Unique: yXc3G-BIM96NWtNjUY4Lgg-1 Received: by mail-oo1-f72.google.com with SMTP id 006d021491bc7-5a05c6e6715so2516953eaf.0 for ; Fri, 01 Mar 2024 11:24:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1709321085; x=1709925885; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Dl0hG1gfpdXDNBj9QgzH3/3Rjape0SFJCzj0uqKWpeQ=; b=lPw00iXLJcPaxLSgFRwF4PT0/u50Uif/S/xGyQUsgPO0Por0B7BAeLuo5j4Ecd8s7u ZbLy3i+tqfJvxSCVv52MHmNuuCXOstlKxr0Cmr3M5Dl3Rq4J3LL59BjenPNWJXSOe1rB L1S+lhotcNcSCUXgxfhzfkRfd4obov9v0eK2OkRpKcNJwiHc/jHXbNA+7b+6n/b20cqe uPXVELpuxSH2QxS1b/9iJAc9lx1t8ZoRSBumEx8//x9HhXHQfOuR3xvAir0c0EbXKD/H kWEijAhsmh1B/vJmBAZI1aeosTApj0VVtTlhEgXwGGxMTh+MzPxKyKUMYcmjJr4fX2Ad 2YwQ== X-Gm-Message-State: AOJu0Ywy1Wm9U3POkmdmcP6RwKX5g8i50WkqP19JBHZYkB8ndt634ePi Zi2pRkqkqfcYrxc8lCoDtBSZ2kxIDUxDM+CGMI7+Qyq7u01spp703g63oa9NW7lKf0m03ySq9Yy OpJA/mHaczI/oO3rJsaCTH0bJGkskNUjQsaNHo3HtRf21rr5VgUM6PoEWRO58248= X-Received: by 2002:a05:6358:b3d0:b0:17b:5b6d:32c2 with SMTP id pb16-20020a056358b3d000b0017b5b6d32c2mr3419849rwc.8.1709321084818; Fri, 01 Mar 2024 11:24:44 -0800 (PST) X-Received: by 2002:a05:6358:b3d0:b0:17b:5b6d:32c2 with SMTP id pb16-20020a056358b3d000b0017b5b6d32c2mr3419810rwc.8.1709321084122; Fri, 01 Mar 2024 11:24:44 -0800 (PST) Received: from redhat.com (2603-7000-9500-34a5-0000-0000-0000-1db4.res6.spectrum.com. [2603:7000:9500:34a5::1db4]) by smtp.gmail.com with ESMTPSA id pa7-20020a056214480700b0068f8a21a065sm2175572qvb.13.2024.03.01.11.24.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 Mar 2024 11:24:43 -0800 (PST) Date: Fri, 1 Mar 2024 14:24:42 -0500 From: Marek Polacek To: Jason Merrill Cc: GCC Patches Subject: [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358] Message-ID: References: <20240126013736.70125-1-polacek@redhat.com> <0e4c47b6-604c-4d30-b458-825959c0e1d6@redhat.com> MIME-Version: 1.0 In-Reply-To: User-Agent: Mutt/2.2.12 (2023-09-09) X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-12.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1789115088088171040 X-GMAIL-MSGID: 1792353138897205384 On Fri, Mar 01, 2024 at 01:19:40PM -0500, Jason Merrill wrote: > On 3/1/24 12:39, Marek Polacek wrote: > > @option{-Wdangling-reference} also warns about code like > > @smallexample > > @@ -3932,6 +3935,10 @@ struct Span @{ > > as @code{std::span}-like; that is, the class is a non-union class > > that has a pointer data member and a trivial destructor. > > +The warning can be disabled by using the @code{gnu::no_dangling} attribute > > +on a function (@pxref{Common Function Attributes}), or a class type > > +(@pxref{C++ Attributes}). > > It seems surprising that one is in a generic attributes section and the > other in the C++-specific section. Maybe both uses could be covered in the > C++ attributes section? Arg yes, definitely. Done here. > > This warning is enabled by @option{-Wall}. > > @opindex Wdelete-non-virtual-dtor > > diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling1.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling1.C > > new file mode 100644 > > index 00000000000..02eabbc5003 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling1.C > > @@ -0,0 +1,38 @@ > > +// { dg-do compile { target c++11 } } > > +// { dg-options "-Wdangling-reference" } > > + > > +int g = 42; > > + > > +struct [[gnu::no_dangling]] A { > > + int *i; > > + int &foo() { return *i; } > > +}; > > + > > +struct A2 { > > + int *i; > > + [[gnu::no_dangling]] int &foo() { return *i; } > > + [[gnu::no_dangling]] static int &bar (const int &) { return *&g; } > > +}; > > + > > +union [[gnu::no_dangling]] U { }; > > + > > +A a() { return A{&g}; } > > +A2 a2() { return A2{&g}; } > > + > > +class X { }; > > +const X x1; > > +const X x2; > > + > > +[[gnu::no_dangling]] const X& get(const int& i) > > +{ > > + return i == 0 ? x1 : x2; > > +} > > + > > +void > > +test () > > +{ > > + [[maybe_unused]] const X& x = get (10); // { dg-bogus "dangling" } > > + [[maybe_unused]] const int &i = a().foo(); // { dg-bogus "dangling" } > > + [[maybe_unused]] const int &j = a2().foo(); // { dg-bogus "dangling" } > > + [[maybe_unused]] const int &k = a2().bar(10); // { dg-bogus "dangling" } > > +} > > Do you want to add destructors to A/A2 like you did in other tests? Added. I think this test predates the recent heuristic. Ok for trunk? -- >8 -- Since -Wdangling-reference has false positives that can't be prevented, we should offer an easy way to suppress the warning. Currently, that is only possible by using a #pragma, either around the enclosing class or around the call site. But #pragma GCC diagnostic tend to be onerous. A better solution would be to have an attribute. To that end, this patch adds a new attribute, [[gnu::no_dangling]]. This attribute takes an optional bool argument to support cases like: template struct [[gnu::no_dangling(std::is_reference_v)]] S { // ... }; PR c++/110358 PR c++/109642 gcc/cp/ChangeLog: * call.cc (no_dangling_p): New. (reference_like_class_p): Use it. (do_warn_dangling_reference): Use it. Don't warn when the function or its enclosing class has attribute gnu::no_dangling. * tree.cc (cxx_gnu_attributes): Add gnu::no_dangling. (handle_no_dangling_attribute): New. gcc/ChangeLog: * doc/extend.texi: Document gnu::no_dangling. * doc/invoke.texi: Mention that gnu::no_dangling disables -Wdangling-reference. gcc/testsuite/ChangeLog: * g++.dg/ext/attr-no-dangling1.C: New test. * g++.dg/ext/attr-no-dangling2.C: New test. * g++.dg/ext/attr-no-dangling3.C: New test. * g++.dg/ext/attr-no-dangling4.C: New test. * g++.dg/ext/attr-no-dangling5.C: New test. * g++.dg/ext/attr-no-dangling6.C: New test. * g++.dg/ext/attr-no-dangling7.C: New test. * g++.dg/ext/attr-no-dangling8.C: New test. * g++.dg/ext/attr-no-dangling9.C: New test. --- gcc/cp/call.cc | 38 ++++++++++-- gcc/cp/tree.cc | 26 ++++++++ gcc/doc/extend.texi | 47 ++++++++++++++ gcc/doc/invoke.texi | 6 ++ gcc/testsuite/g++.dg/ext/attr-no-dangling1.C | 40 ++++++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling2.C | 29 +++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling3.C | 24 ++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling4.C | 14 +++++ gcc/testsuite/g++.dg/ext/attr-no-dangling5.C | 31 ++++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling6.C | 65 ++++++++++++++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling7.C | 31 ++++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling8.C | 30 +++++++++ gcc/testsuite/g++.dg/ext/attr-no-dangling9.C | 25 ++++++++ 13 files changed, 400 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling1.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling2.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling3.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling4.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling5.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling6.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling7.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling8.C create mode 100644 gcc/testsuite/g++.dg/ext/attr-no-dangling9.C base-commit: 64221c7bffbdd399e49554b0fb08b38325657596 diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index c40ef2e3028..9e4c8073600 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -14033,11 +14033,7 @@ std_pair_ref_ref_p (tree t) return true; } -/* Return true if a class CTYPE is either std::reference_wrapper or - std::ref_view, or a reference wrapper class. We consider a class - a reference wrapper class if it has a reference member. We no - longer check that it has a constructor taking the same reference type - since that approach still generated too many false positives. */ +/* Return true if a class T has a reference member. */ static bool class_has_reference_member_p (tree t) @@ -14061,12 +14057,41 @@ class_has_reference_member_p_r (tree binfo, void *) ? integer_one_node : NULL_TREE); } + +/* Return true if T (either a class or a function) has been marked as + not-dangling. */ + +static bool +no_dangling_p (tree t) +{ + t = lookup_attribute ("no_dangling", TYPE_ATTRIBUTES (t)); + if (!t) + return false; + + t = TREE_VALUE (t); + if (!t) + return true; + + t = build_converted_constant_bool_expr (TREE_VALUE (t), tf_warning_or_error); + t = cxx_constant_value (t); + return t == boolean_true_node; +} + +/* Return true if a class CTYPE is either std::reference_wrapper or + std::ref_view, or a reference wrapper class. We consider a class + a reference wrapper class if it has a reference member. We no + longer check that it has a constructor taking the same reference type + since that approach still generated too many false positives. */ + static bool reference_like_class_p (tree ctype) { if (!CLASS_TYPE_P (ctype)) return false; + if (no_dangling_p (ctype)) + return true; + /* Also accept a std::pair. */ if (std_pair_ref_ref_p (ctype)) return true; @@ -14173,7 +14198,8 @@ do_warn_dangling_reference (tree expr, bool arg_p) but probably not to one of its arguments. */ || (DECL_OBJECT_MEMBER_FUNCTION_P (fndecl) && DECL_OVERLOADED_OPERATOR_P (fndecl) - && DECL_OVERLOADED_OPERATOR_IS (fndecl, INDIRECT_REF))) + && DECL_OVERLOADED_OPERATOR_IS (fndecl, INDIRECT_REF)) + || no_dangling_p (TREE_TYPE (fndecl))) return NULL_TREE; tree rettype = TREE_TYPE (TREE_TYPE (fndecl)); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index ad312710f68..e75be9a4e66 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -47,6 +47,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *); static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *); static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *); static tree handle_contract_attribute (tree *, tree, tree, int, bool *); +static tree handle_no_dangling_attribute (tree *, tree, tree, int, bool *); /* If REF is an lvalue, returns the kind of lvalue that REF is. Otherwise, returns clk_none. */ @@ -5102,6 +5103,8 @@ static const attribute_spec cxx_gnu_attributes[] = handle_init_priority_attribute, NULL }, { "abi_tag", 1, -1, false, false, false, true, handle_abi_tag_attribute, NULL }, + { "no_dangling", 0, 1, false, true, false, false, + handle_no_dangling_attribute, NULL }, }; const scoped_attribute_specs cxx_gnu_attribute_table = @@ -5391,6 +5394,29 @@ handle_contract_attribute (tree *ARG_UNUSED (node), tree ARG_UNUSED (name), return NULL_TREE; } +/* Handle a "no_dangling" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_no_dangling_attribute (tree *node, tree name, tree args, int, + bool *no_add_attrs) +{ + if (args && TREE_CODE (TREE_VALUE (args)) == STRING_CST) + { + error ("%qE attribute argument must be an expression that evaluates " + "to true or false", name); + *no_add_attrs = true; + } + else if (!FUNC_OR_METHOD_TYPE_P (*node) + && !RECORD_OR_UNION_TYPE_P (*node)) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + /* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the thing pointed to by the constant. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 6c2c7ae5d8a..8e1751eae6c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -29327,6 +29327,53 @@ Some_Class B __attribute__ ((init_priority (543))); Note that the particular values of @var{priority} do not matter; only their relative ordering. +@cindex @code{no_dangling} type attribute +@cindex @code{no_dangling} function attribute +@item no_dangling + +This attribute can be applied on a class type, function, or member +function. Dangling references to classes marked with this attribute +will have the @option{-Wdangling-reference} diagnostic suppressed; so +will the @code{gnu::no_dangling}-marked functions. For example: + +@smallexample +class [[gnu::no_dangling]] S @{ @dots{} @}; +@end smallexample + +Or: + +@smallexample +class A @{ + int *p; + [[gnu::no_dangling]] int &foo() @{ return *p; @} +@}; + +[[gnu::no_dangling]] const int & +foo (const int &i) +@{ + @dots{} +@} +@end smallexample + +This attribute takes an optional argument, which must be an expression that +evaluates to true or false: + +@smallexample +template +struct [[gnu::no_dangling(std::is_reference_v)]] S @{ + @dots{} +@}; +@end smallexample + +Or: + +@smallexample +template +[[gnu::no_dangling(std::is_reference_v)]] int foo (T& t) @{ + @dots{} +@}; +@end smallexample + @cindex @code{warn_unused} type attribute @item warn_unused diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index dc5fd863ca4..bdf05be387d 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -3908,6 +3908,9 @@ const T& foo (const T&) @{ @dots{} @} #pragma GCC diagnostic pop @end smallexample +The @code{#pragma} can also surround the class; in that case, the warning +will be disabled for all the member functions. + @option{-Wdangling-reference} also warns about code like @smallexample @@ -3932,6 +3935,9 @@ struct Span @{ as @code{std::span}-like; that is, the class is a non-union class that has a pointer data member and a trivial destructor. +The warning can be disabled by using the @code{gnu::no_dangling} attribute +(@pxref{C++ Attributes}). + This warning is enabled by @option{-Wall}. @opindex Wdelete-non-virtual-dtor diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling1.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling1.C new file mode 100644 index 00000000000..dff34e89436 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling1.C @@ -0,0 +1,40 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-Wdangling-reference" } + +int g = 42; + +struct [[gnu::no_dangling]] A { + ~A(); + int *i; + int &foo() { return *i; } +}; + +struct A2 { + ~A2(); + int *i; + [[gnu::no_dangling]] int &foo() { return *i; } + [[gnu::no_dangling]] static int &bar (const int &) { return *&g; } +}; + +union [[gnu::no_dangling]] U { }; + +A a() { return A{&g}; } +A2 a2() { return A2{&g}; } + +class X { }; +const X x1; +const X x2; + +[[gnu::no_dangling]] const X& get(const int& i) +{ + return i == 0 ? x1 : x2; +} + +void +test () +{ + [[maybe_unused]] const X& x = get (10); // { dg-bogus "dangling" } + [[maybe_unused]] const int &i = a().foo(); // { dg-bogus "dangling" } + [[maybe_unused]] const int &j = a2().foo(); // { dg-bogus "dangling" } + [[maybe_unused]] const int &k = a2().bar(10); // { dg-bogus "dangling" } +} diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling2.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling2.C new file mode 100644 index 00000000000..4cdc97ea7c4 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling2.C @@ -0,0 +1,29 @@ +// { dg-do compile { target c++11 } } +// Negative tests. + +struct [[no_dangling]] A { // { dg-warning "ignored" } + [[no_dangling]] int &foo (int &); // { dg-warning "ignored" } +}; + +[[no_dangling]] int &bar (int &); // { dg-warning "ignored" } + +[[gnu::no_dangling]] int i; // { dg-warning "ignored" } +[[gnu::no_dangling]] double d; // { dg-warning "ignored" } +[[gnu::no_dangling]] typedef int T; // { dg-warning "ignored" } + +[[gnu::no_dangling()]] int &fn1 (int &); // { dg-error "parentheses" } +[[gnu::no_dangling("a")]] int &fn2 (int &); // { dg-error "must be an expression" } +[[gnu::no_dangling(true, true)]] int &fn3 (int &); // { dg-error "wrong number of arguments" } + +enum [[gnu::no_dangling]] E { // { dg-warning "ignored" } + X [[gnu::no_dangling]] // { dg-warning "ignored" } +}; + +[[gnu::no_dangling]]; // { dg-warning "ignored" } + +void +g () +{ + goto L; +[[gnu::no_dangling]] L:; // { dg-warning "ignored" } +} diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling3.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling3.C new file mode 100644 index 00000000000..764b104fd3c --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling3.C @@ -0,0 +1,24 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-Wdangling-reference" } + +template +struct [[gnu::no_dangling]] Span { + T* data_; + int len_; + // So that our heuristic doesn't suppress the warning anyway. + ~Span(); + + [[nodiscard]] constexpr auto operator[](int n) const noexcept -> T& { return data_[n]; } + [[nodiscard]] constexpr auto front() const noexcept -> T& { return data_[0]; } + [[nodiscard]] constexpr auto back() const noexcept -> T& { return data_[len_ - 1]; } +}; + +auto get() -> Span; + +auto f() -> int { + int const& a = get().front(); // { dg-bogus "dangling" } + int const& b = get().back(); // { dg-bogus "dangling" } + int const& c = get()[0]; // { dg-bogus "dangling" } + + return a + b + c; +} diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling4.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling4.C new file mode 100644 index 00000000000..e910723d985 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling4.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++11 } } + +#if !__has_attribute(no_dangling) +#error unsupported +#endif + +#ifdef __has_cpp_attribute +# if !__has_cpp_attribute(no_dangling) +# error no_dangling +# endif +#endif + +struct [[gnu::no_dangling]] S { }; +static_assert (__builtin_has_attribute (S, no_dangling), ""); diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling5.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling5.C new file mode 100644 index 00000000000..ec5075482c4 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling5.C @@ -0,0 +1,31 @@ +// PR c++/110358 +// { dg-do compile { target c++20 } } +// { dg-options "-Wdangling-reference" } + +template +struct Span { + T* data_; + int len_; + ~Span(); + + [[nodiscard]] constexpr auto operator[](int n) const noexcept -> T& { return data_[n]; } +}; + +template <> +struct [[gnu::no_dangling]] Span { + int* data_; + int len_; + ~Span(); + + [[nodiscard]] constexpr auto operator[](int n) const noexcept -> int& { return data_[n]; } +}; + +auto getch() -> Span; +auto geti() -> Span; + +void +f () +{ + [[maybe_unused]] const auto &a = getch()[0]; // { dg-warning "dangling reference" } + [[maybe_unused]] const auto &b = geti()[0]; // { dg-bogus "dangling reference" } +} diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling6.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling6.C new file mode 100644 index 00000000000..235a5fd86c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling6.C @@ -0,0 +1,65 @@ +// PR c++/110358 +// { dg-do compile { target c++20 } } +// { dg-options "-Wdangling-reference" } + +class X { }; +const X x1; +const X x2; + +constexpr bool val () { return true; } +struct ST { static constexpr bool value = true; }; +struct SF { static constexpr bool value = false; }; + +template +[[gnu::no_dangling(T::value)]] +const X& get (const int& i) +{ + return i == 0 ? x1 : x2; +} + +template +[[gnu::no_dangling(B)]] +const X& foo (const int& i) +{ + return i == 0 ? x1 : x2; +} + +[[gnu::no_dangling(val ())]] +const X& bar (const int& i) +{ + return i == 0 ? x1 : x2; +} + +[[gnu::no_dangling(!val ())]] +const X& baz (const int& i) +{ + return i == 0 ? x1 : x2; +} + +template +struct [[gnu::no_dangling(T::value)]] +Span { + T* data_; + int len_; + ~Span(); + + [[nodiscard]] constexpr auto operator[](int n) const noexcept -> T& { return data_[n]; } +}; + +auto geti() -> Span; +auto gety() -> Span; + +void +test () +{ + [[maybe_unused]] const X& x1 = get (10); // { dg-bogus "dangling" } + [[maybe_unused]] const X& x2 = get (10); // { dg-warning "dangling" } + [[maybe_unused]] const X& x3 = foo (10); // { dg-bogus "dangling" } + [[maybe_unused]] const X& x4 = foo (10); // { dg-warning "dangling" } + [[maybe_unused]] const X& x7 = foo<> (10); // { dg-bogus "dangling" } + [[maybe_unused]] const X& x5 = bar (10); // { dg-bogus "dangling" } + [[maybe_unused]] const X& x6 = baz (10); // { dg-warning "dangling" } + + [[maybe_unused]] const auto &b1 = geti()[0]; // { dg-bogus "dangling" } + [[maybe_unused]] const auto &b2 = gety()[0]; // { dg-warning "dangling" } +} diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling7.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling7.C new file mode 100644 index 00000000000..3c392ed409f --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling7.C @@ -0,0 +1,31 @@ +// PR c++/110358 +// { dg-do compile { target c++20 } } +// { dg-options "-Wdangling-reference" } + +class X { }; +const X x1; +const X x2; + +template +[[gnu::no_dangling(N)]] const X& get(const int& i); // { dg-error "parameter packs not expanded" } + +template +[[gnu::no_dangling(T::x)]] // { dg-error "member" } +const X& foo(const int& i); + +bool val () { return true; } + +[[gnu::no_dangling(val ())]] // { dg-error "call" } +const X& bar (const int& i); + +[[gnu::no_dangling(20)]] const X& fn1 (const int &); + +void +test () +{ + [[maybe_unused]] const X& x1 = bar (10); // { dg-warning "dangling" } + [[maybe_unused]] const X& x2 = foo (10); // { dg-error "no matching" } + [[maybe_unused]] const X& x3 // { dg-warning "dangling" } + = fn1 (10); // { dg-error "narrowing" } +} + diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling8.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling8.C new file mode 100644 index 00000000000..8208d751a4b --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling8.C @@ -0,0 +1,30 @@ +// PR c++/110358 +// { dg-do compile { target c++20 } } +// { dg-options "-Wdangling-reference" } + +template constexpr bool is_reference_v = false; +template constexpr bool is_reference_v = true; +template constexpr bool is_reference_v = true; + +template +struct [[gnu::no_dangling(is_reference_v)]] S { + int &foo (const int &); +}; + +template +struct X { + template + struct [[gnu::no_dangling(is_reference_v && is_reference_v)]] Y { + int &foo (const int &); + }; +}; + +void +g () +{ + [[maybe_unused]] const int &x0 = S().foo (42); // { dg-bogus "dangling" } + [[maybe_unused]] const int &x1 = S().foo (42); // { dg-warning "dangling" } + [[maybe_unused]] const auto &x2 = X::Y<>().foo (42); // { dg-warning "dangling" } + [[maybe_unused]] const auto &x3 = X::Y<>().foo (42); // { dg-bogus "dangling" } +} + diff --git a/gcc/testsuite/g++.dg/ext/attr-no-dangling9.C b/gcc/testsuite/g++.dg/ext/attr-no-dangling9.C new file mode 100644 index 00000000000..65b4f7145a9 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-no-dangling9.C @@ -0,0 +1,25 @@ +// PR c++/110358 +// { dg-do compile { target c++20 } } +// { dg-options "-Wdangling-reference" } + +template +struct bool_constant { + static constexpr bool value = B; + constexpr operator bool() const { return value; } +}; + +using true_type = bool_constant; +using false_type = bool_constant; + +struct S { + template + [[gnu::no_dangling(B)]] int &foo (const int &); +}; + +void +g () +{ + [[maybe_unused]] const int &x0 = S().foo (42); // { dg-warning "dangling" } + [[maybe_unused]] const int &x1 = S().foo (42); // { dg-bogus "dangling" } +} +