[v4] c++: implement [[gnu::non_owning]] [PR110358]

Message ID ZeIS0IjmUuMN8v_b@redhat.com
State Unresolved
Headers
Series [v4] c++: implement [[gnu::non_owning]] [PR110358] |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Marek Polacek March 1, 2024, 5:39 p.m. UTC
  On Thu, Feb 29, 2024 at 07:30:02PM -0500, Jason Merrill wrote:
> On 2/29/24 19:12, Marek Polacek wrote:
> > On Wed, Feb 28, 2024 at 06:03:54PM -0500, Jason Merrill wrote:
> > 
> > > Hmm, if we're also going to allow the attribute to be applied to a function,
> > > the name doesn't make so much sense.  For a class, it says that the class
> > > refers to its initializer; for a function, it says that the function return
> > > value *doesn't* refer to its argument.
> > 
> > Yeah, that's a fair point; I guess "non_owning" would be too perplexing.
> > 
> > > If we want something that can apply to both classes and functions, we're
> > > probably back to an attribute that just suppresses the warning, with a
> > > different name.
> > > 
> > > Or I guess we could have two attributes, but that seems like a lot.
> > > 
> > > WDYT?
> > 
> > I think we don't want two separate attributes, and we do want that one
> > attribute to apply to both fns and classes.  We could implement something
> > like
> > 
> >    [[gnu::no_warning("Wdangling-reference")]]
> >    [[gnu::no_warning("Wdangling-reference", bool)]]
> > 
> > but first, that's a lot of typing, second, it would be confusing because
> > it wouldn't work for any other warning.  We already have [[unused]] and
> > [[maybe_unused]] whose effect is to suppress a warning.  It think our
> > best bet is to do the most straightforward thing: [[gnu::no_dangling]],
> > which this patch implements.  I didn't call it no_dangling_reference in
> > the hope that it can, some day, be also used for some -Wdangling-pointer
> > purposes.
> > 
> > 
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, 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 <typename T>
> >    struct [[gnu::no_dangling(std::is_reference_v<T>)]] 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                          | 21 +++++++
> >   gcc/doc/invoke.texi                          | 21 +++++++
> >   gcc/testsuite/g++.dg/ext/attr-no-dangling1.C | 38 ++++++++++++
> >   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, 387 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
> > 
> > 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<const T&, const T&>.  */
> >     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 efd78014d1a..571655bf39a 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -29317,6 +29317,27 @@ 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
> > +@item no_dangling
> > +
> > +This attribute can be applied on a class type, function, or member
> > +function.  Entities marked with this attribute will have the
> > +@option{-Wdangling-reference} diagnostic suppressed.
> > +
> > +@smallexample
> > +class [[gnu::no_dangling]] S @{ @dots{} @};
> > +@end smallexample
> > +
> > +This attribute takes an optional argument, which must be an expression that
> > +evaluates to true or false:
> > +
> > +@smallexample
> > +template <typename T>
> > +struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
> > +  @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 7862c751801..9022a413dd4 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,24 @@ 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,
> 
> Can this be an xref, and put all the documentation for the usage and effect
> of the attribute in the attribute section?

Yes.  And I need an entry both for the type and function attribute.
 
> > +which can be applied on the enclosing class type (in which case it disables
> > +the warning for all its member functions),
> 
> I think this misrepresents the effect of putting the attribute on the class.
> Rather, it means that we won't warn about a dangling reference to the class.

Updated, NFC:

-- >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 <typename T>
  struct [[gnu::no_dangling(std::is_reference_v<T>)]] 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                          | 51 +++++++++++++++
 gcc/doc/invoke.texi                          |  7 +++
 gcc/testsuite/g++.dg/ext/attr-no-dangling1.C | 38 ++++++++++++
 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, 403 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: 4894c82b0c3cf0d6ec4bc1e96709b6140ec11f6e
  

Comments

Jason Merrill March 1, 2024, 6:19 p.m. UTC | #1
On 3/1/24 12:39, Marek Polacek wrote:
> On Thu, Feb 29, 2024 at 07:30:02PM -0500, Jason Merrill wrote:
>> On 2/29/24 19:12, Marek Polacek wrote:
>>> On Wed, Feb 28, 2024 at 06:03:54PM -0500, Jason Merrill wrote:
>>>
>>>> Hmm, if we're also going to allow the attribute to be applied to a function,
>>>> the name doesn't make so much sense.  For a class, it says that the class
>>>> refers to its initializer; for a function, it says that the function return
>>>> value *doesn't* refer to its argument.
>>>
>>> Yeah, that's a fair point; I guess "non_owning" would be too perplexing.
>>>
>>>> If we want something that can apply to both classes and functions, we're
>>>> probably back to an attribute that just suppresses the warning, with a
>>>> different name.
>>>>
>>>> Or I guess we could have two attributes, but that seems like a lot.
>>>>
>>>> WDYT?
>>>
>>> I think we don't want two separate attributes, and we do want that one
>>> attribute to apply to both fns and classes.  We could implement something
>>> like
>>>
>>>     [[gnu::no_warning("Wdangling-reference")]]
>>>     [[gnu::no_warning("Wdangling-reference", bool)]]
>>>
>>> but first, that's a lot of typing, second, it would be confusing because
>>> it wouldn't work for any other warning.  We already have [[unused]] and
>>> [[maybe_unused]] whose effect is to suppress a warning.  It think our
>>> best bet is to do the most straightforward thing: [[gnu::no_dangling]],
>>> which this patch implements.  I didn't call it no_dangling_reference in
>>> the hope that it can, some day, be also used for some -Wdangling-pointer
>>> purposes.
>>>
>>>
>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, 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 <typename T>
>>>     struct [[gnu::no_dangling(std::is_reference_v<T>)]] 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                          | 21 +++++++
>>>    gcc/doc/invoke.texi                          | 21 +++++++
>>>    gcc/testsuite/g++.dg/ext/attr-no-dangling1.C | 38 ++++++++++++
>>>    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, 387 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
>>>
>>> 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<const T&, const T&>.  */
>>>      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 efd78014d1a..571655bf39a 100644
>>> --- a/gcc/doc/extend.texi
>>> +++ b/gcc/doc/extend.texi
>>> @@ -29317,6 +29317,27 @@ 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
>>> +@item no_dangling
>>> +
>>> +This attribute can be applied on a class type, function, or member
>>> +function.  Entities marked with this attribute will have the
>>> +@option{-Wdangling-reference} diagnostic suppressed.
>>> +
>>> +@smallexample
>>> +class [[gnu::no_dangling]] S @{ @dots{} @};
>>> +@end smallexample
>>> +
>>> +This attribute takes an optional argument, which must be an expression that
>>> +evaluates to true or false:
>>> +
>>> +@smallexample
>>> +template <typename T>
>>> +struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
>>> +  @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 7862c751801..9022a413dd4 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,24 @@ 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,
>>
>> Can this be an xref, and put all the documentation for the usage and effect
>> of the attribute in the attribute section?
> 
> Yes.  And I need an entry both for the type and function attribute.
>   
>>> +which can be applied on the enclosing class type (in which case it disables
>>> +the warning for all its member functions),
>>
>> I think this misrepresents the effect of putting the attribute on the class.
>> Rather, it means that we won't warn about a dangling reference to the class.
> 
> Updated, NFC:
> 
> -- >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 <typename T>
>    struct [[gnu::no_dangling(std::is_reference_v<T>)]] 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                          | 51 +++++++++++++++
>   gcc/doc/invoke.texi                          |  7 +++
>   gcc/testsuite/g++.dg/ext/attr-no-dangling1.C | 38 ++++++++++++
>   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, 403 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
> 
> 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<const T&, const T&>.  */
>     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..5bd3933f011 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -4380,6 +4380,36 @@ visibility of their template.
>   If both the template and enclosing class have explicit visibility, the
>   visibility from the template is used.
>   
> +@cindex @code{no_dangling} function attribute
> +@item no_dangling
> +
> +The @code{gnu::no_dangling} attribute can be used to suppress the
> +@option{-Wdangling-reference} diagnostic on a function or a member
> +function.  For example:
> +
> +@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 <typename T>
> +[[gnu::no_dangling(std::is_reference_v<T>)]] int foo (T& t) @{
> +  @dots{}
> +@};
> +@end smallexample
> +
>   @cindex @code{warn_unused_result} function attribute
>   @item warn_unused_result
>   The @code{warn_unused_result} attribute causes a warning to be emitted
> @@ -29327,6 +29357,27 @@ 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
> +@item no_dangling
> +
> +This attribute can be applied on a class type.  Dangling references to
> +classes marked with this attribute will have the @option{-Wdangling-reference}
> +diagnostic suppressed.
> +
> +@smallexample
> +class [[gnu::no_dangling]] S @{ @dots{} @};
> +@end smallexample
> +
> +This attribute takes an optional argument, which must be an expression that
> +evaluates to true or false:
> +
> +@smallexample
> +template <typename T>
> +struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
> +  @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..0422b2c400a 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,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?

>   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?

> 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 <typename T>
> +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<int>;
> +
> +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 <typename T>
> +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> {
> +    int* data_;
> +    int len_;
> +    ~Span();
> +
> +    [[nodiscard]] constexpr auto operator[](int n) const noexcept -> int& { return data_[n]; }
> +};
> +
> +auto getch() -> Span<char>;
> +auto geti() -> Span<int>;
> +
> +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<typename T>
> +[[gnu::no_dangling(T::value)]]
> +const X& get (const int& i)
> +{
> +   return i == 0 ? x1 : x2;
> +}
> +
> +template<bool B = true>
> +[[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 <typename T>
> +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<ST>;
> +auto gety() -> Span<SF>;
> +
> +void
> +test ()
> +{
> +  [[maybe_unused]] const X& x1 = get<ST> (10);	  // { dg-bogus "dangling" }
> +  [[maybe_unused]] const X& x2 = get<SF> (10);	  // { dg-warning "dangling" }
> +  [[maybe_unused]] const X& x3 = foo<true> (10);  // { dg-bogus "dangling" }
> +  [[maybe_unused]] const X& x4 = foo<false> (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<bool... N>
> +[[gnu::no_dangling(N)]] const X& get(const int& i); // { dg-error "parameter packs not expanded" }
> +
> +template<typename T>
> +[[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<int> (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<class T> constexpr bool is_reference_v = false;
> +template<class T> constexpr bool is_reference_v<T&> = true;
> +template<class T> constexpr bool is_reference_v<T&&> = true;
> +
> +template <typename T>
> +struct [[gnu::no_dangling(is_reference_v<T>)]] S {
> +  int &foo (const int &);
> +};
> +
> +template <typename T1, typename T2>
> +struct X {
> +  template <typename U1 = T1, typename U2 = T2>
> +  struct [[gnu::no_dangling(is_reference_v<U1> && is_reference_v<U2>)]] Y {
> +    int &foo (const int &);
> +  };
> +};
> +
> +void
> +g ()
> +{
> +  [[maybe_unused]] const int &x0 = S<int&>().foo (42);  // { dg-bogus "dangling" }
> +  [[maybe_unused]] const int &x1 = S<int>().foo (42);   // { dg-warning "dangling" }
> +  [[maybe_unused]] const auto &x2 = X<int, int&>::Y<>().foo (42); // { dg-warning "dangling" }
> +  [[maybe_unused]] const auto &x3 = X<int&, int&>::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<bool B>
> +struct bool_constant {
> +  static constexpr bool value = B;
> +  constexpr operator bool() const { return value; }
> +};
> +
> +using true_type = bool_constant<true>;
> +using false_type = bool_constant<false>;
> +
> +struct S {
> +  template<bool B>
> +  [[gnu::no_dangling(B)]] int &foo (const int &);
> +};
> +
> +void
> +g ()
> +{
> +  [[maybe_unused]] const int &x0 = S().foo<false_type{}> (42);  // { dg-warning "dangling" }
> +  [[maybe_unused]] const int &x1 = S().foo<true_type{}> (42);  // { dg-bogus "dangling" }
> +}
> +
> 
> base-commit: 4894c82b0c3cf0d6ec4bc1e96709b6140ec11f6e
  

Patch

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<const T&, const T&>.  */
   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..5bd3933f011 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -4380,6 +4380,36 @@  visibility of their template.
 If both the template and enclosing class have explicit visibility, the
 visibility from the template is used.
 
+@cindex @code{no_dangling} function attribute
+@item no_dangling
+
+The @code{gnu::no_dangling} attribute can be used to suppress the
+@option{-Wdangling-reference} diagnostic on a function or a member
+function.  For example:
+
+@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 <typename T>
+[[gnu::no_dangling(std::is_reference_v<T>)]] int foo (T& t) @{
+  @dots{}
+@};
+@end smallexample
+
 @cindex @code{warn_unused_result} function attribute
 @item warn_unused_result
 The @code{warn_unused_result} attribute causes a warning to be emitted
@@ -29327,6 +29357,27 @@  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
+@item no_dangling
+
+This attribute can be applied on a class type.  Dangling references to
+classes marked with this attribute will have the @option{-Wdangling-reference}
+diagnostic suppressed.
+
+@smallexample
+class [[gnu::no_dangling]] S @{ @dots{} @};
+@end smallexample
+
+This attribute takes an optional argument, which must be an expression that
+evaluates to true or false:
+
+@smallexample
+template <typename T>
+struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
+  @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..0422b2c400a 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,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}).
+
 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" }
+}
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 <typename T>
+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<int>;
+
+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 <typename T>
+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> {
+    int* data_;
+    int len_;
+    ~Span();
+
+    [[nodiscard]] constexpr auto operator[](int n) const noexcept -> int& { return data_[n]; }
+};
+
+auto getch() -> Span<char>;
+auto geti() -> Span<int>;
+
+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<typename T>
+[[gnu::no_dangling(T::value)]]
+const X& get (const int& i)
+{
+   return i == 0 ? x1 : x2;
+}
+
+template<bool B = true>
+[[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 <typename T>
+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<ST>;
+auto gety() -> Span<SF>;
+
+void
+test ()
+{
+  [[maybe_unused]] const X& x1 = get<ST> (10);	  // { dg-bogus "dangling" }
+  [[maybe_unused]] const X& x2 = get<SF> (10);	  // { dg-warning "dangling" }
+  [[maybe_unused]] const X& x3 = foo<true> (10);  // { dg-bogus "dangling" }
+  [[maybe_unused]] const X& x4 = foo<false> (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<bool... N>
+[[gnu::no_dangling(N)]] const X& get(const int& i); // { dg-error "parameter packs not expanded" }
+
+template<typename T>
+[[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<int> (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<class T> constexpr bool is_reference_v = false;
+template<class T> constexpr bool is_reference_v<T&> = true;
+template<class T> constexpr bool is_reference_v<T&&> = true;
+
+template <typename T>
+struct [[gnu::no_dangling(is_reference_v<T>)]] S {
+  int &foo (const int &);
+};
+
+template <typename T1, typename T2>
+struct X {
+  template <typename U1 = T1, typename U2 = T2>
+  struct [[gnu::no_dangling(is_reference_v<U1> && is_reference_v<U2>)]] Y {
+    int &foo (const int &);
+  };
+};
+
+void
+g ()
+{
+  [[maybe_unused]] const int &x0 = S<int&>().foo (42);  // { dg-bogus "dangling" }
+  [[maybe_unused]] const int &x1 = S<int>().foo (42);   // { dg-warning "dangling" }
+  [[maybe_unused]] const auto &x2 = X<int, int&>::Y<>().foo (42); // { dg-warning "dangling" }
+  [[maybe_unused]] const auto &x3 = X<int&, int&>::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<bool B>
+struct bool_constant {
+  static constexpr bool value = B;
+  constexpr operator bool() const { return value; }
+};
+
+using true_type = bool_constant<true>;
+using false_type = bool_constant<false>;
+
+struct S {
+  template<bool B>
+  [[gnu::no_dangling(B)]] int &foo (const int &);
+};
+
+void
+g ()
+{
+  [[maybe_unused]] const int &x0 = S().foo<false_type{}> (42);  // { dg-warning "dangling" }
+  [[maybe_unused]] const int &x1 = S().foo<true_type{}> (42);  // { dg-bogus "dangling" }
+}
+