* [PATCH] c++: implement [[gnu::non_owning]] [PR110358]
@ 2024-01-26 1:37 Marek Polacek
2024-01-26 2:34 ` Marek Polacek
2024-01-26 21:04 ` Jason Merrill
0 siblings, 2 replies; 15+ messages in thread
From: Marek Polacek @ 2024-01-26 1:37 UTC (permalink / raw)
To: GCC Patches, Jason Merrill
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. Such
an attribute should not be tied to this particular warning though. [*]
The warning bogusly triggers for classes that are like std::span,
std::reference_wrapper, and std::ranges::ref_view. The common property
seems to be that these classes are only wrappers around some data. So
I chose the name non_owning, but I'm not attached to it. I hope that
in the future the attribute can be used for something other than this
diagnostic.
[*] As I'm typing this, it's occurring to me that we might consider
having a general attribute allowing users to do [[gnu::ignore("-Wfoo")]].
PR c++/110358
PR c++/109642
gcc/cp/ChangeLog:
* call.cc (do_warn_dangling_reference): Don't warn when the function
or its enclosing class has attribute gnu::non_owning.
* tree.cc (cxx_gnu_attributes): Add gnu::non_owning.
(handle_non_owning_attribute): New.
gcc/ChangeLog:
* doc/extend.texi: Document gnu::non_owning.
* doc/invoke.texi: Mention that gnu::non_owning disables
-Wdangling-reference.
gcc/testsuite/ChangeLog:
* g++.dg/ext/attr-non-owning1.C: New test.
* g++.dg/ext/attr-non-owning2.C: New test.
* g++.dg/ext/attr-non-owning3.C: New test.
* g++.dg/ext/attr-non-owning4.C: New test.
* g++.dg/ext/attr-non-owning5.C: New test.
---
gcc/cp/call.cc | 9 ++++-
gcc/cp/tree.cc | 20 +++++++++++
gcc/doc/extend.texi | 15 ++++++++
gcc/doc/invoke.texi | 21 ++++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning1.C | 38 +++++++++++++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning2.C | 28 +++++++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning3.C | 24 +++++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning4.C | 14 ++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning5.C | 29 ++++++++++++++++
9 files changed, 197 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning1.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning2.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning3.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning4.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning5.C
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 9de0d77c423..88ddba825a9 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -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.
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 77f57e0f9ac..2adf59b22d4 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_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. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 18c485c11b0..09e68cc8759 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -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
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 6ec56493e59..9986c1add39 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning1.C b/gcc/testsuite/g++.dg/ext/attr-non-owning1.C
new file mode 100644
index 00000000000..000f7f4019d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning1.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning2.C b/gcc/testsuite/g++.dg/ext/attr-non-owning2.C
new file mode 100644
index 00000000000..6f1d649736a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning2.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning3.C b/gcc/testsuite/g++.dg/ext/attr-non-owning3.C
new file mode 100644
index 00000000000..81c6491643b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning3.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning4.C b/gcc/testsuite/g++.dg/ext/attr-non-owning4.C
new file mode 100644
index 00000000000..c50382c3e8f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning4.C
@@ -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), "");
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning5.C b/gcc/testsuite/g++.dg/ext/attr-non-owning5.C
new file mode 100644
index 00000000000..6122d583ccc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning5.C
@@ -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" }
+}
base-commit: fd620bd3351c6b9821c299035ed17e655d7954b5
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] c++: implement [[gnu::non_owning]] [PR110358]
2024-01-26 1:37 [PATCH] c++: implement [[gnu::non_owning]] [PR110358] Marek Polacek
@ 2024-01-26 2:34 ` Marek Polacek
2024-01-26 21:04 ` Jason Merrill
1 sibling, 0 replies; 15+ messages in thread
From: Marek Polacek @ 2024-01-26 2:34 UTC (permalink / raw)
To: GCC Patches, Jason Merrill
On Thu, Jan 25, 2024 at 08:37:36PM -0500, Marek Polacek wrote:
> +/* 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)
I overlooked error: unused parameter 'args'. Fixed.
Marek
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] c++: implement [[gnu::non_owning]] [PR110358]
2024-01-26 1:37 [PATCH] c++: implement [[gnu::non_owning]] [PR110358] Marek Polacek
2024-01-26 2:34 ` Marek Polacek
@ 2024-01-26 21:04 ` Jason Merrill
2024-02-22 0:35 ` [PATCH v2] " Marek Polacek
1 sibling, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2024-01-26 21:04 UTC (permalink / raw)
To: Marek Polacek, GCC Patches
On 1/25/24 20:37, Marek Polacek wrote:
> 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. Such
> an attribute should not be tied to this particular warning though. [*]
>
> The warning bogusly triggers for classes that are like std::span,
> std::reference_wrapper, and std::ranges::ref_view. The common property
> seems to be that these classes are only wrappers around some data. So
> I chose the name non_owning, but I'm not attached to it. I hope that
> in the future the attribute can be used for something other than this
> diagnostic.
You decided not to pursue Barry's request for a bool argument to the
attribute?
Might it be more useful for the attribute to make reference_like_class_p
return true, so that we still warn about a temporary of another type
passing through it?
Jason
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2] c++: implement [[gnu::non_owning]] [PR110358]
2024-01-26 21:04 ` Jason Merrill
@ 2024-02-22 0:35 ` Marek Polacek
2024-02-28 23:03 ` Jason Merrill
0 siblings, 1 reply; 15+ messages in thread
From: Marek Polacek @ 2024-02-22 0:35 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Fri, Jan 26, 2024 at 04:04:35PM -0500, Jason Merrill wrote:
> On 1/25/24 20:37, Marek Polacek wrote:
> > 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. Such
> > an attribute should not be tied to this particular warning though. [*]
> >
> > The warning bogusly triggers for classes that are like std::span,
> > std::reference_wrapper, and std::ranges::ref_view. The common property
> > seems to be that these classes are only wrappers around some data. So
> > I chose the name non_owning, but I'm not attached to it. I hope that
> > in the future the attribute can be used for something other than this
> > diagnostic.
>
> You decided not to pursue Barry's request for a bool argument to the
> attribute?
At first I thought it'd be an unnecessary complication but it was actually
pretty easy. Better to accept the optional argument from the get-go
otherwise people would have to add > GCC 14 checks.
> Might it be more useful for the attribute to make reference_like_class_p
> return true, so that we still warn about a temporary of another type passing
> through it?
Good point. Fixed.
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. Such
an attribute should not be tied to this particular warning though.
The warning bogusly triggers for classes that are like std::span,
std::reference_wrapper, and std::ranges::ref_view. The common property
seems to be that these classes are only wrappers around some data. So
I chose the name non_owning, but I'm not attached to it. I hope that
in the future the attribute can be used for something other than this
diagnostic.
This attribute takes an optional bool argument to support cases like:
template <typename T>
struct [[gnu::non_owning(std::is_reference_v<T>)]] S {
// ...
};
PR c++/110358
PR c++/109642
gcc/cp/ChangeLog:
* call.cc (non_owning_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::non_owning.
* tree.cc (cxx_gnu_attributes): Add gnu::non_owning.
(handle_non_owning_attribute): New.
gcc/ChangeLog:
* doc/extend.texi: Document gnu::non_owning.
* doc/invoke.texi: Mention that gnu::non_owning disables
-Wdangling-reference.
gcc/testsuite/ChangeLog:
* g++.dg/ext/attr-non-owning1.C: New test.
* g++.dg/ext/attr-non-owning2.C: New test.
* g++.dg/ext/attr-non-owning3.C: New test.
* g++.dg/ext/attr-non-owning4.C: New test.
* g++.dg/ext/attr-non-owning5.C: New test.
* g++.dg/ext/attr-non-owning6.C: New test.
* g++.dg/ext/attr-non-owning7.C: New test.
* g++.dg/ext/attr-non-owning8.C: New test.
* g++.dg/ext/attr-non-owning9.C: New test.
---
gcc/cp/call.cc | 38 ++++++++++--
gcc/cp/tree.cc | 26 +++++++++
gcc/doc/extend.texi | 25 ++++++++
gcc/doc/invoke.texi | 21 +++++++
gcc/testsuite/g++.dg/ext/attr-non-owning1.C | 38 ++++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning2.C | 29 +++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning3.C | 24 ++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning4.C | 14 +++++
gcc/testsuite/g++.dg/ext/attr-non-owning5.C | 31 ++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning6.C | 65 +++++++++++++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning7.C | 31 ++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning8.C | 30 ++++++++++
gcc/testsuite/g++.dg/ext/attr-non-owning9.C | 25 ++++++++
13 files changed, 391 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning1.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning2.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning3.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning4.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning5.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning6.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning7.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning8.C
create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning9.C
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 1dac1470d3b..e4bf9c963bd 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
+ non-owning. */
+
+static bool
+non_owning_p (tree t)
+{
+ t = lookup_attribute ("non_owning", 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 (non_owning_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))
+ || non_owning_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..017da7e294a 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_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. */
@@ -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 },
+ { "non_owning", 0, 1, false, true, false, false,
+ handle_non_owning_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 "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 (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 2135dfde9c8..9132add8267 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -29320,6 +29320,31 @@ 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
+
+This attribute takes an optional argument, which must be an expression that
+evaluates to true or false:
+
+@smallexample
+template <typename T>
+struct [[gnu::non_owning(std::is_reference_v<T>)]] 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
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b4e4ee9fb81..0df2e2ded34 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::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
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning1.C b/gcc/testsuite/g++.dg/ext/attr-non-owning1.C
new file mode 100644
index 00000000000..000f7f4019d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning1.C
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning2.C b/gcc/testsuite/g++.dg/ext/attr-non-owning2.C
new file mode 100644
index 00000000000..4d2037e8b78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning2.C
@@ -0,0 +1,29 @@
+// { 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 "parentheses" }
+[[gnu::non_owning("a")]] int &fn2 (int &); // { dg-error "must be an expression" }
+[[gnu::non_owning(true, true)]] int &fn3 (int &); // { dg-error "wrong number of 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" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning3.C b/gcc/testsuite/g++.dg/ext/attr-non-owning3.C
new file mode 100644
index 00000000000..81c6491643b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning3.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning4.C b/gcc/testsuite/g++.dg/ext/attr-non-owning4.C
new file mode 100644
index 00000000000..c50382c3e8f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning4.C
@@ -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), "");
diff --git a/gcc/testsuite/g++.dg/ext/attr-non-owning5.C b/gcc/testsuite/g++.dg/ext/attr-non-owning5.C
new file mode 100644
index 00000000000..d297bb615fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning5.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::non_owning]] 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-non-owning6.C b/gcc/testsuite/g++.dg/ext/attr-non-owning6.C
new file mode 100644
index 00000000000..10ce1842852
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning6.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::non_owning(T::value)]]
+const X& get (const int& i)
+{
+ return i == 0 ? x1 : x2;
+}
+
+template<bool B = true>
+[[gnu::non_owning(B)]]
+const X& foo (const int& i)
+{
+ return i == 0 ? x1 : x2;
+}
+
+[[gnu::non_owning(val ())]]
+const X& bar (const int& i)
+{
+ return i == 0 ? x1 : x2;
+}
+
+[[gnu::non_owning(!val ())]]
+const X& baz (const int& i)
+{
+ return i == 0 ? x1 : x2;
+}
+
+template <typename T>
+struct [[gnu::non_owning(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-non-owning7.C b/gcc/testsuite/g++.dg/ext/attr-non-owning7.C
new file mode 100644
index 00000000000..bba69871918
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning7.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::non_owning(N)]] const X& get(const int& i); // { dg-error "parameter packs not expanded" }
+
+template<typename T>
+[[gnu::non_owning(T::x)]] // { dg-error "member" }
+const X& foo(const int& i);
+
+bool val () { return true; }
+
+[[gnu::non_owning(val ())]] // { dg-error "call" }
+const X& bar (const int& i);
+
+[[gnu::non_owning(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-non-owning8.C b/gcc/testsuite/g++.dg/ext/attr-non-owning8.C
new file mode 100644
index 00000000000..1bfed4ed33c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning8.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::non_owning(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::non_owning(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-non-owning9.C b/gcc/testsuite/g++.dg/ext/attr-non-owning9.C
new file mode 100644
index 00000000000..6f6a72736ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/attr-non-owning9.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::non_owning(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: 98004ca00e4bf7a513cf3de65d3c3d9ad373872e
--
2.43.2
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] c++: implement [[gnu::non_owning]] [PR110358]
2024-02-22 0:35 ` [PATCH v2] " Marek Polacek
@ 2024-02-28 23:03 ` Jason Merrill
2024-03-01 0:12 ` [PATCH v3] " Marek Polacek
0 siblings, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2024-02-28 23:03 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 2/21/24 19:35, Marek Polacek wrote:
> On Fri, Jan 26, 2024 at 04:04:35PM -0500, Jason Merrill wrote:
>> On 1/25/24 20:37, Marek Polacek wrote:
>>> 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. Such
>>> an attribute should not be tied to this particular warning though. [*]
>>>
>>> The warning bogusly triggers for classes that are like std::span,
>>> std::reference_wrapper, and std::ranges::ref_view. The common property
>>> seems to be that these classes are only wrappers around some data. So
>>> I chose the name non_owning, but I'm not attached to it. I hope that
>>> in the future the attribute can be used for something other than this
>>> diagnostic.
>>
>> You decided not to pursue Barry's request for a bool argument to the
>> attribute?
>
> At first I thought it'd be an unnecessary complication but it was actually
> pretty easy. Better to accept the optional argument from the get-go
> otherwise people would have to add > GCC 14 checks.
>
>> Might it be more useful for the attribute to make reference_like_class_p
>> return true, so that we still warn about a temporary of another type passing
>> through it?
>
> Good point. Fixed.
>
> 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. Such
> an attribute should not be tied to this particular warning though.
>
> The warning bogusly triggers for classes that are like std::span,
> std::reference_wrapper, and std::ranges::ref_view. The common property
> seems to be that these classes are only wrappers around some data. So
> I chose the name non_owning, but I'm not attached to it. I hope that
> in the future the attribute can be used for something other than this
> diagnostic.
>
> This attribute takes an optional bool argument to support cases like:
>
> template <typename T>
> struct [[gnu::non_owning(std::is_reference_v<T>)]] S {
> // ...
> };
>
> PR c++/110358
> PR c++/109642
>
> gcc/cp/ChangeLog:
>
> * call.cc (non_owning_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::non_owning.
> * tree.cc (cxx_gnu_attributes): Add gnu::non_owning.
> (handle_non_owning_attribute): New.
>
> gcc/ChangeLog:
>
> * doc/extend.texi: Document gnu::non_owning.
> * doc/invoke.texi: Mention that gnu::non_owning disables
> -Wdangling-reference.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/ext/attr-non-owning1.C: New test.
> * g++.dg/ext/attr-non-owning2.C: New test.
> * g++.dg/ext/attr-non-owning3.C: New test.
> * g++.dg/ext/attr-non-owning4.C: New test.
> * g++.dg/ext/attr-non-owning5.C: New test.
> * g++.dg/ext/attr-non-owning6.C: New test.
> * g++.dg/ext/attr-non-owning7.C: New test.
> * g++.dg/ext/attr-non-owning8.C: New test.
> * g++.dg/ext/attr-non-owning9.C: New test.
> ---
> gcc/cp/call.cc | 38 ++++++++++--
> gcc/cp/tree.cc | 26 +++++++++
> gcc/doc/extend.texi | 25 ++++++++
> gcc/doc/invoke.texi | 21 +++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning1.C | 38 ++++++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning2.C | 29 +++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning3.C | 24 ++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning4.C | 14 +++++
> gcc/testsuite/g++.dg/ext/attr-non-owning5.C | 31 ++++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning6.C | 65 +++++++++++++++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning7.C | 31 ++++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning8.C | 30 ++++++++++
> gcc/testsuite/g++.dg/ext/attr-non-owning9.C | 25 ++++++++
> 13 files changed, 391 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning1.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning2.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning3.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning4.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning5.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning6.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning7.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning8.C
> create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning9.C
>
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index 1dac1470d3b..e4bf9c963bd 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
> + non-owning. */
> +
> +static bool
> +non_owning_p (tree t)
> +{
> + t = lookup_attribute ("non_owning", 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 (non_owning_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))
> + || non_owning_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..017da7e294a 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_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. */
> @@ -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 },
> + { "non_owning", 0, 1, false, true, false, false,
> + handle_non_owning_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 "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 (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 2135dfde9c8..9132add8267 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -29320,6 +29320,31 @@ 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
> +
> +This attribute takes an optional argument, which must be an expression that
> +evaluates to true or false:
> +
> +@smallexample
> +template <typename T>
> +struct [[gnu::non_owning(std::is_reference_v<T>)]] 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
>
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index b4e4ee9fb81..0df2e2ded34 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::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:
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.
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?
Jason
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3] c++: implement [[gnu::non_owning]] [PR110358]
2024-02-28 23:03 ` Jason Merrill
@ 2024-03-01 0:12 ` Marek Polacek
2024-03-01 0:30 ` Jason Merrill
0 siblings, 1 reply; 15+ messages in thread
From: Marek Polacek @ 2024-03-01 0:12 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Wed, Feb 28, 2024 at 06:03:54PM -0500, Jason Merrill wrote:
> On 2/21/24 19:35, Marek Polacek wrote:
> > On Fri, Jan 26, 2024 at 04:04:35PM -0500, Jason Merrill wrote:
> > > On 1/25/24 20:37, Marek Polacek wrote:
> > > > 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. Such
> > > > an attribute should not be tied to this particular warning though. [*]
> > > >
> > > > The warning bogusly triggers for classes that are like std::span,
> > > > std::reference_wrapper, and std::ranges::ref_view. The common property
> > > > seems to be that these classes are only wrappers around some data. So
> > > > I chose the name non_owning, but I'm not attached to it. I hope that
> > > > in the future the attribute can be used for something other than this
> > > > diagnostic.
> > >
> > > You decided not to pursue Barry's request for a bool argument to the
> > > attribute?
> >
> > At first I thought it'd be an unnecessary complication but it was actually
> > pretty easy. Better to accept the optional argument from the get-go
> > otherwise people would have to add > GCC 14 checks.
> > > Might it be more useful for the attribute to make reference_like_class_p
> > > return true, so that we still warn about a temporary of another type passing
> > > through it?
> >
> > Good point. Fixed.
> >
> > 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. Such
> > an attribute should not be tied to this particular warning though.
> >
> > The warning bogusly triggers for classes that are like std::span,
> > std::reference_wrapper, and std::ranges::ref_view. The common property
> > seems to be that these classes are only wrappers around some data. So
> > I chose the name non_owning, but I'm not attached to it. I hope that
> > in the future the attribute can be used for something other than this
> > diagnostic.
> >
> > This attribute takes an optional bool argument to support cases like:
> >
> > template <typename T>
> > struct [[gnu::non_owning(std::is_reference_v<T>)]] S {
> > // ...
> > };
> >
> > PR c++/110358
> > PR c++/109642
> >
> > gcc/cp/ChangeLog:
> >
> > * call.cc (non_owning_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::non_owning.
> > * tree.cc (cxx_gnu_attributes): Add gnu::non_owning.
> > (handle_non_owning_attribute): New.
> >
> > gcc/ChangeLog:
> >
> > * doc/extend.texi: Document gnu::non_owning.
> > * doc/invoke.texi: Mention that gnu::non_owning disables
> > -Wdangling-reference.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/ext/attr-non-owning1.C: New test.
> > * g++.dg/ext/attr-non-owning2.C: New test.
> > * g++.dg/ext/attr-non-owning3.C: New test.
> > * g++.dg/ext/attr-non-owning4.C: New test.
> > * g++.dg/ext/attr-non-owning5.C: New test.
> > * g++.dg/ext/attr-non-owning6.C: New test.
> > * g++.dg/ext/attr-non-owning7.C: New test.
> > * g++.dg/ext/attr-non-owning8.C: New test.
> > * g++.dg/ext/attr-non-owning9.C: New test.
> > ---
> > gcc/cp/call.cc | 38 ++++++++++--
> > gcc/cp/tree.cc | 26 +++++++++
> > gcc/doc/extend.texi | 25 ++++++++
> > gcc/doc/invoke.texi | 21 +++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning1.C | 38 ++++++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning2.C | 29 +++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning3.C | 24 ++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning4.C | 14 +++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning5.C | 31 ++++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning6.C | 65 +++++++++++++++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning7.C | 31 ++++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning8.C | 30 ++++++++++
> > gcc/testsuite/g++.dg/ext/attr-non-owning9.C | 25 ++++++++
> > 13 files changed, 391 insertions(+), 6 deletions(-)
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning1.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning2.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning3.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning4.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning5.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning6.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning7.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning8.C
> > create mode 100644 gcc/testsuite/g++.dg/ext/attr-non-owning9.C
> >
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index 1dac1470d3b..e4bf9c963bd 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
> > + non-owning. */
> > +
> > +static bool
> > +non_owning_p (tree t)
> > +{
> > + t = lookup_attribute ("non_owning", 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 (non_owning_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))
> > + || non_owning_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..017da7e294a 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_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. */
> > @@ -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 },
> > + { "non_owning", 0, 1, false, true, false, false,
> > + handle_non_owning_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 "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 (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 2135dfde9c8..9132add8267 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -29320,6 +29320,31 @@ 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
> > +
> > +This attribute takes an optional argument, which must be an expression that
> > +evaluates to true or false:
> > +
> > +@smallexample
> > +template <typename T>
> > +struct [[gnu::non_owning(std::is_reference_v<T>)]] 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
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index b4e4ee9fb81..0df2e2ded34 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::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:
>
> 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,
+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::no_dangling]] A @{
+ int *p;
+ int &foo() @{ return *p; @}
+@};
+
+[[gnu::no_dangling]] const int &
+foo (const int &i)
+@{
+ @dots{}
+@}
+@end smallexample
+
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" }
+}
+
base-commit: cda3836161834c5f21f264885891fe4d0ce90da1
--
2.43.2
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 0:12 ` [PATCH v3] " Marek Polacek
@ 2024-03-01 0:30 ` Jason Merrill
2024-03-01 17:39 ` [PATCH v4] " Marek Polacek
0 siblings, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2024-03-01 0:30 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
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?
> +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.
> member function, or a regular
> +function. For example:
Jason
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 0:30 ` Jason Merrill
@ 2024-03-01 17:39 ` Marek Polacek
2024-03-01 18:19 ` Jason Merrill
0 siblings, 1 reply; 15+ messages in thread
From: Marek Polacek @ 2024-03-01 17:39 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
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}).
+
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" }
+}
+
base-commit: 4894c82b0c3cf0d6ec4bc1e96709b6140ec11f6e
--
2.44.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 17:39 ` [PATCH v4] " Marek Polacek
@ 2024-03-01 18:19 ` Jason Merrill
2024-03-01 19:24 ` [PATCH v5] " Marek Polacek
0 siblings, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2024-03-01 18:19 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
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
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 18:19 ` Jason Merrill
@ 2024-03-01 19:24 ` Marek Polacek
2024-03-01 20:38 ` Jason Merrill
0 siblings, 1 reply; 15+ messages in thread
From: Marek Polacek @ 2024-03-01 19:24 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
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 <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 | 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
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..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 <typename T>
+struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
+ @dots{}
+@};
+@end smallexample
+
+Or:
+
+@smallexample
+template <typename T>
+[[gnu::no_dangling(std::is_reference_v<T>)]] 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 <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: 64221c7bffbdd399e49554b0fb08b38325657596
--
2.44.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 19:24 ` [PATCH v5] " Marek Polacek
@ 2024-03-01 20:38 ` Jason Merrill
2024-03-01 21:23 ` Patrick Palka
2024-03-04 11:00 ` Jonathan Wakely
0 siblings, 2 replies; 15+ messages in thread
From: Jason Merrill @ 2024-03-01 20:38 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 3/1/24 14:24, Marek Polacek wrote:
> 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 <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 | 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
>
> 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..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:
...; so will references returned from...
> +@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 <typename T>
> +struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
> + @dots{}
> +@};
> +@end smallexample
> +
> +Or:
> +
> +@smallexample
> +template <typename T>
> +[[gnu::no_dangling(std::is_reference_v<T>)]] int foo (T& t) @{
I think this function should return a reference.
OK with those changes, thanks.
Jason
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 20:38 ` Jason Merrill
@ 2024-03-01 21:23 ` Patrick Palka
2024-03-01 21:31 ` Jason Merrill
2024-03-04 11:00 ` Jonathan Wakely
1 sibling, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2024-03-01 21:23 UTC (permalink / raw)
To: Jason Merrill; +Cc: Marek Polacek, GCC Patches
On Fri, 1 Mar 2024, Jason Merrill wrote:
> On 3/1/24 14:24, Marek Polacek wrote:
> > 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 <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 | 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
> >
> > 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))
Sorry for not asking this sooner, but does it matter whether we attach
the attribute to the function type rather than the function declaration?
I noticed e.g. nodiscard gets attached to the decl.
> > + {
> > + 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
And we document it as a function attribute despite attaching it to the
function type.
> > +@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:
>
> ...; so will references returned from...
>
> > +@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 <typename T>
> > +struct [[gnu::no_dangling(std::is_reference_v<T>)]] S @{
> > + @dots{}
> > +@};
> > +@end smallexample
> > +
> > +Or:
> > +
> > +@smallexample
> > +template <typename T>
> > +[[gnu::no_dangling(std::is_reference_v<T>)]] int foo (T& t) @{
>
> I think this function should return a reference.
>
> OK with those changes, thanks.
>
> Jason
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 21:23 ` Patrick Palka
@ 2024-03-01 21:31 ` Jason Merrill
0 siblings, 0 replies; 15+ messages in thread
From: Jason Merrill @ 2024-03-01 21:31 UTC (permalink / raw)
To: Patrick Palka; +Cc: Marek Polacek, GCC Patches
On 3/1/24 16:23, Patrick Palka wrote:
>
> Sorry for not asking this sooner, but does it matter whether we attach
> the attribute to the function type rather than the function declaration?
> I noticed e.g. nodiscard gets attached to the decl.
>
> And we document it as a function attribute despite attaching it to the
> function type.
I think it doesn't matter much, some attributes are represented on the
type and some on the decl. Might be a bit better on the decl but I
wasn't worrying about it.
Jason
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-01 20:38 ` Jason Merrill
2024-03-01 21:23 ` Patrick Palka
@ 2024-03-04 11:00 ` Jonathan Wakely
2024-03-04 16:30 ` Marek Polacek
1 sibling, 1 reply; 15+ messages in thread
From: Jonathan Wakely @ 2024-03-04 11:00 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches, Jason Merrill
On 01/03/24 15:38 -0500, Jason Merrill wrote:
>On 3/1/24 14:24, Marek Polacek wrote:
>>+@smallexample
>>+template <typename T>
>>+[[gnu::no_dangling(std::is_reference_v<T>)]] int foo (T& t) @{
>
>I think this function should return a reference.
The condition in the attribute can only ever be true if you call this
function with an explicit template argument list: foo<int&>(i). Is
that intentional?
And if T is non-const it can't be called with a temporary and so
dangling seems less of a problem for this function anyway, right?
Would it make more sense as something like this?
template <typename T>
[[gnu::no_dangling(std::is_lvalue_reference_v<T>)]]
decltype(auto) foo(T&& t) {
...
}
Or is this getting too complex/subtle for a simple example?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5] c++: implement [[gnu::non_owning]] [PR110358]
2024-03-04 11:00 ` Jonathan Wakely
@ 2024-03-04 16:30 ` Marek Polacek
0 siblings, 0 replies; 15+ messages in thread
From: Marek Polacek @ 2024-03-04 16:30 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: GCC Patches, Jason Merrill
On Mon, Mar 04, 2024 at 11:00:18AM +0000, Jonathan Wakely wrote:
> On 01/03/24 15:38 -0500, Jason Merrill wrote:
> > On 3/1/24 14:24, Marek Polacek wrote:
> > > +@smallexample
> > > +template <typename T>
> > > +[[gnu::no_dangling(std::is_reference_v<T>)]] int foo (T& t) @{
> >
> > I think this function should return a reference.
>
> The condition in the attribute can only ever be true if you call this
> function with an explicit template argument list: foo<int&>(i). Is
> that intentional?
Not intentional. I just wanted to make it clear that the user
can use something like std::is_reference as the attribute argument,
but I didn't think about it very long.
> And if T is non-const it can't be called with a temporary and so
> dangling seems less of a problem for this function anyway, right?
Right.
> Would it make more sense as something like this?
>
> template <typename T>
> [[gnu::no_dangling(std::is_lvalue_reference_v<T>)]]
> decltype(auto) foo(T&& t) {
> ...
> }
>
> Or is this getting too complex/subtle for a simple example?
I like your example; it's only slightly more complex than the
original one and most likely more realistic. I'm pushing the
following patch. Thanks!
[pushed] doc: update [[gnu::no_dangling]]
...to offer a more realistic example.
gcc/ChangeLog:
* doc/extend.texi: Update [[gnu::no_dangling]].
---
gcc/doc/extend.texi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f679c81acf2..df0982fdfda 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -29370,7 +29370,8 @@ Or:
@smallexample
template <typename T>
-[[gnu::no_dangling(std::is_reference_v<T>)]] int& foo (T& t) @{
+[[gnu::no_dangling(std::is_lvalue_reference_v<T>)]]
+decltype(auto) foo(T&& t) @{
@dots{}
@};
@end smallexample
base-commit: 77eb86be8841989651b3150a020dd1a95910cc00
--
2.44.0
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2024-03-04 16:30 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-26 1:37 [PATCH] c++: implement [[gnu::non_owning]] [PR110358] Marek Polacek
2024-01-26 2:34 ` Marek Polacek
2024-01-26 21:04 ` Jason Merrill
2024-02-22 0:35 ` [PATCH v2] " Marek Polacek
2024-02-28 23:03 ` Jason Merrill
2024-03-01 0:12 ` [PATCH v3] " Marek Polacek
2024-03-01 0:30 ` Jason Merrill
2024-03-01 17:39 ` [PATCH v4] " Marek Polacek
2024-03-01 18:19 ` Jason Merrill
2024-03-01 19:24 ` [PATCH v5] " Marek Polacek
2024-03-01 20:38 ` Jason Merrill
2024-03-01 21:23 ` Patrick Palka
2024-03-01 21:31 ` Jason Merrill
2024-03-04 11:00 ` Jonathan Wakely
2024-03-04 16:30 ` Marek Polacek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).