@@ -14157,9 +14157,16 @@ 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))
+ || lookup_attribute ("non_owning",
+ TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
return NULL_TREE;
+ if (tree ctx = CP_DECL_CONTEXT (fndecl))
+ if (TYPE_P (ctx)
+ && lookup_attribute ("non_owning", TYPE_ATTRIBUTES (ctx)))
+ return NULL_TREE;
+
tree rettype = TREE_TYPE (TREE_TYPE (fndecl));
/* If the function doesn't return a reference, don't warn. This
can be e.g.
@@ -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_non_owning_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -5096,6 +5097,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 },
+ { "non_owning", 0, 0, false, true, false, false,
+ handle_non_owning_attribute, NULL },
};
const scoped_attribute_specs cxx_gnu_attribute_table =
@@ -5385,6 +5388,23 @@ handle_contract_attribute (tree *ARG_UNUSED (node), tree ARG_UNUSED (name),
return NULL_TREE;
}
+/* Handle a "non_owning" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+tree
+handle_non_owning_attribute (tree *node, tree name, tree args, int,
+ bool *no_add_attrs)
+{
+ 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. */
@@ -29299,6 +29299,21 @@ Some_Class B __attribute__ ((init_priority (543)));
Note that the particular values of @var{priority} do not matter; only their
relative ordering.
+@cindex @code{non_owning} type attribute
+@item non_owning
+
+This attribute can be applied on a class type, function, or member
+function and indicates that it does not own its associated data. For
+example, classes like @code{std::span} or @code{std::reference_wrapper}
+are considered non-owning.
+
+@smallexample
+class [[gnu::non_owning]] S @{ @dots{} @};
+@end smallexample
+
+Currently, the only effect this attribute has is to suppress the
+@option{-Wdangling-reference} diagnostic.
+
@cindex @code{warn_unused} type attribute
@item warn_unused
@@ -3906,6 +3906,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
@@ -3916,6 +3919,24 @@ where @code{std::minmax} returns @code{std::pair<const int&, const int&>}, and
both references dangle after the end of the full expression that contains
the call to @code{std::minmax}.
+The warning can be disabled by using the @code{gnu::non_owning} attribute,
+which can be applied on the enclosing class type (in which case it disables
+the warning for all its member functions), member function, or a regular
+function. For example:
+
+@smallexample
+class [[gnu::non_owning]] A @{
+ int *p;
+ int &foo() @{ return *p; @}
+@};
+
+[[gnu::non_owning]] const int &
+foo (const int &i)
+@{
+ @dots{}
+@}
+@end smallexample
+
This warning is enabled by @option{-Wall}.
@opindex Wdelete-non-virtual-dtor
new file mode 100644
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdangling-reference" }
+
+int g = 42;
+
+struct [[gnu::non_owning]] A {
+ int *i;
+ int &foo() { return *i; }
+};
+
+struct A2 {
+ int *i;
+ [[gnu::non_owning]] int &foo() { return *i; }
+ [[gnu::non_owning]] static int &bar (const int &) { return *&g; }
+};
+
+union [[gnu::non_owning]] U { };
+
+A a() { return A{&g}; }
+A2 a2() { return A2{&g}; }
+
+class X { };
+const X x1;
+const X x2;
+
+[[gnu::non_owning]] 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" }
+}
new file mode 100644
@@ -0,0 +1,28 @@
+// { dg-do compile { target c++11 } }
+// Negative tests.
+
+struct [[non_owning]] A { // { dg-warning "ignored" }
+ [[non_owning]] int &foo (int &); // { dg-warning "ignored" }
+};
+
+[[non_owning]] int &bar (int &); // { dg-warning "ignored" }
+
+[[gnu::non_owning]] int i; // { dg-warning "ignored" }
+[[gnu::non_owning]] double d; // { dg-warning "ignored" }
+[[gnu::non_owning]] typedef int T; // { dg-warning "ignored" }
+
+[[gnu::non_owning()]] int &fn1 (int &); // { dg-error "attribute does not take any arguments" }
+[[gnu::non_owning("a")]] int &fn2 (int &); // { dg-error "attribute does not take any arguments" }
+
+enum [[gnu::non_owning]] E { // { dg-warning "ignored" }
+ X [[gnu::non_owning]] // { dg-warning "ignored" }
+};
+
+[[gnu::non_owning]]; // { dg-warning "ignored" }
+
+void
+g ()
+{
+ goto L;
+[[gnu::non_owning]] L:; // { dg-warning "ignored" }
+}
new file mode 100644
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdangling-reference" }
+
+template <typename T>
+struct [[gnu::non_owning]] 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;
+}
new file mode 100644
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+
+#if !__has_attribute(non_owning)
+#error unsupported
+#endif
+
+#ifdef __has_cpp_attribute
+# if !__has_cpp_attribute(non_owning)
+# error non_owning
+# endif
+#endif
+
+struct [[gnu::non_owning]] S { };
+static_assert (__builtin_has_attribute (S, non_owning), "");
new file mode 100644
@@ -0,0 +1,29 @@
+// PR c++/110358
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wdangling-reference" }
+
+template <typename T>
+struct Span {
+ T* data_;
+ int len_;
+
+ [[nodiscard]] constexpr auto operator[](int n) const noexcept -> T& { return data_[n]; }
+};
+
+template <>
+struct [[gnu::non_owning]] Span<int> {
+ int* data_;
+ int len_;
+
+ [[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" }
+}