On 10/26/22 12:10, Marek Polacek wrote:
On Tue, Oct 25, 2022 at 11:53:51AM -0400, Jason Merrill via Gcc-patches wrote:
On 10/25/22 11:21, Marek Polacek wrote:
On Mon, Oct 24, 2022 at 01:30:42PM -0400, Jason Merrill wrote:
On 10/21/22 19:28, Marek Polacek wrote:
It doesn't warn when the function in question is a member function, otherwise
it'd emit loads of warnings for valid code like obj.emplace<T>({0}, 0).
We had discussed warning if the object argument is a temporary (and for the
above check, the function returns *this)?
Presumably you mean detecting something like this:
struct S {
const S& self () { return *this; }
};
const S& s = S().self();
Yes. Or
struct S {
int ar[4];
int& operator[](int i) { return ar[i]; }
};
const int &r = S()[2];
Ok, I've extended the warning to check if the object argument is a temporary
as well, so we get a warning here now.
I don't currently have a way to detect it, can I steal a METHOD_TYPE flag
that says "this member function returns *this"? Alternatively, walk its
DECL_SAVED_TREE and look for RETURN_EXPR?
Like you limited the above check to TREE_STATIC, let's also forget about
checking for return *this.
I don't follow, but it looks like I don't need to change anything and just
keep the TREE_STATIC check?
It warns in member initializer lists as well:
const int& f(const int& i) { return i; }
struct S {
const int &r; // { dg-warning "dangling reference" }
S() : r(f(10)) { } // { dg-message "destroyed" }
};
I've run the testsuite/bootstrap with the warning enabled by default.
There were just a few FAILs:
* g++.dg/warn/Wdangling-pointer-2.C
* 20_util/any/misc/any_cast.cc
* 20_util/forward/c_neg.cc
* 20_util/forward/f_neg.cc
* experimental/any/misc/any_cast.cc
all of these look like genuine bugs. A bootstrap with the warning
enabled by default passed.
When testing a previous version of the patch, there were many FAILs in
libstdc++'s 22_locale/; all of them because the warning triggered on
const test_type& obj = std::use_facet<test_type>(std::locale());
but this code looks valid -- std::use_facet doesn't return a reference
to its parameter. Therefore I added code to suppress the warning when
the call is std::use_facet. Now 22_locale/* pass even with the warning
on. We could exclude more std:: functions like this if desirable.
Instead of adding special cases in the compiler, let's disable the warning
around the definition of use_facet (and adjust the compiler as needed so
that avoids the warning).
As I said in
<https://gcc.gnu.org/pipermail/gcc-patches/2022-October/604307.html>
I don't think it's possible without inventing an attribute (?).
Replied.
Fixed.
+ tree fndecl = cp_get_callee_fndecl_nofold (call);
+ if (!fndecl
+ || warning_suppressed_p (fndecl, OPT_Wdangling_reference)
+ /* Don't warn about member functions; the warning would trigger in
+ valid code like
+ std::any a(...);
+ S& s = a.emplace<S>({0}, 0);
+ which constructs a new object and returns a reference to it. */
+ || DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)
+ /* It seems unreasonable to warn about operator functions. */
+ || DECL_OVERLOADED_OPERATOR_P (fndecl)
I guess I'd expect false positives on << and >> because of iostreams, do you
see false positives with other operators?
This was just a guess. The warning triggered in g++.dg/overload/operator6.C.
That looks like a true positive.
Ok, then...
I suppose this could be limited to << and >>? I'm not sure.
I'm not sure either. Another possibility would be to only consider the LHS
of binary operators, since returning a reference to the RHS is very unusual.
...I've removed the DECL_OVERLOADED_OPERATOR_P check altogether and don't
really see any false positives. It's probably better to start with a more
general warning and then tweak it based on empirical evidence.
+// Invalid, but we don't warn here yet.
+// r12 = f (f ((const int &) &TARGET_EXPR <D.2459, 1>))
+const int& r12 = f(f(1));
This should be a simple recursion?
Hmm, the inner call is just a sub-expression of the full-expression so
there you can still use the returned temporary. But in this case the
temporary is used beyond the full-expression so it's invalid. I've added
if (tree r = find_initializing_call_expr (arg))
if (cp_tree_equal (CALL_EXPR_FN (r), CALL_EXPR_FN (expr)))
return expr;
so that we detect f(f(1)) but I'm dubious that this is actually useful.
Indeed, but why limit it to checking for the same function rather than also
warning for
f(g(1))
or
A().f().g()
?
OK, I've made the warning even more recursive so that now we detect
both of these, see Wdangling-reference3.C. While at it, I've renamed
the functions.
As before, I've tested this patch twice, once with -Wdangling-reference
enabled by default, once with -Wdangling-reference enabled by -Wextra.
The couple of FAILs I saw were true positives (e.g., rv-conv1.C, rv-func2.C).
I might actually add it to -Wall, the false positive rate sounds pretty
low at this point.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
This patch implements a new experimental warning (enabled by -Wextra) to
detect references bound to temporaries whose lifetime has ended. The
primary motivation is the Note in
<https://en.cppreference.com/w/cpp/algorithm/max>:
Capturing the result of std::max by reference produces a dangling reference
if one of the parameters is a temporary and that parameter is returned:
int n = 1;
const int& r = std::max(n-1, n+1); // r is dangling
That's because both temporaries for n-1 and n+1 are destroyed at the end
of the full expression. With this warning enabled, you'll get:
g.C:3:12: warning: possibly dangling reference to a temporary
[-Wdangling-reference]
3 | const int& r = std::max(n-1, n+1);
| ^
g.C:3:24: note: the temporary was destroyed at the end of the full expression
'std::max<int>((n - 1), (n + 1))'
3 | const int& r = std::max(n-1, n+1);
| ~~~~~~~~^~~~~~~~~~
The warning works by checking if a reference is initialized with a function
that returns a reference, and at least one parameter of the function is
a reference that is bound to a temporary. It assumes that such a function
actually returns one of its arguments! (I added code to check_return_expr
to suppress the warning when we've seen the definition of the function
and we can say that it can return a variable with static storage
duration.)
It warns when the function in question is a member function, but only if
the function is invoked on a temporary object, otherwise the warning
would emit loads of warnings for valid code like obj.emplace<T>({0}, 0).
It does detect the dangling reference in:
struct S {
const S& self () { return *this; }
};
const S& s = S().self();
It warns in member initializer lists as well:
const int& f(const int& i) { return i; }
struct S {
const int &r;
S() : r(f(10)) { }
};
I've run the testsuite/bootstrap with the warning enabled by default.
There were just a few FAILs, all of which look like genuine bugs.
A bootstrap with the warning enabled by default passed as well.
When testing a previous version of the patch, there were many FAILs in
libstdc++'s 22_locale/; all of them because the warning triggered on
const test_type& obj = std::use_facet<test_type>(std::locale());
but this code looks valid -- std::use_facet doesn't return a reference
to its parameter. Therefore I added a #pragma and code to suppress the
warning.
PR c++/106393
gcc/c-family/ChangeLog:
* c.opt (Wdangling-reference): New.
gcc/cp/ChangeLog:
* call.cc (expr_represents_temporary_p): New, factored out of...
(conv_binds_ref_to_temporary): ...here. Don't return false just
because a ck_base is missing. Use expr_represents_temporary_p.
(do_warn_dangling_reference): New.
(maybe_warn_dangling_reference): New.
(extend_ref_init_temps): Call maybe_warn_dangling_reference.
* typeck.cc (check_return_expr): Suppress -Wdangling-reference
warnings.
gcc/ChangeLog:
* doc/invoke.texi: Document -Wdangling-reference.
libstdc++-v3/ChangeLog:
* include/bits/locale_classes.tcc: Add #pragma to disable
-Wdangling-reference with std::use_facet.
gcc/testsuite/ChangeLog:
* g++.dg/cpp23/elision4.C: Use -Wdangling-reference, add dg-warning.
* g++.dg/cpp23/elision7.C: Likewise.
* g++.dg/warn/Wdangling-reference1.C: New test.
* g++.dg/warn/Wdangling-reference2.C: New test.
* g++.dg/warn/Wdangling-reference3.C: New test.
---
gcc/c-family/c.opt | 4 +
gcc/cp/call.cc | 148 ++++++++++++++++--
gcc/cp/cp-tree.h | 4 +-
gcc/cp/typeck.cc | 10 ++
gcc/doc/invoke.texi | 40 ++++-
gcc/testsuite/g++.dg/cpp23/elision4.C | 5 +-
gcc/testsuite/g++.dg/cpp23/elision7.C | 3 +-
.../g++.dg/warn/Wdangling-reference1.C | 144 +++++++++++++++++
.../g++.dg/warn/Wdangling-reference2.C | 28 ++++
.../g++.dg/warn/Wdangling-reference3.C | 24 +++
libstdc++-v3/include/bits/locale_classes.tcc | 3 +
11 files changed, 396 insertions(+), 17 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference1.C
create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference2.C
create mode 100644 gcc/testsuite/g++.dg/warn/Wdangling-reference3.C
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 01d480759ae..02d79991aeb 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -555,6 +555,10 @@ Wdangling-pointer=
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_dangling_pointer)
Warning LangEnabledBy(C ObjC C++ ObjC++,Wall, 2, 0) IntegerRange(0, 2)
Warn for uses of pointers to auto variables whose lifetime has ended.
+Wdangling-reference
+C++ ObjC++ Var(warn_dangling_reference) Warning LangEnabledBy(C++ ObjC++,
Wextra)
+Warn when a reference is bound to a temporary whose lifetime has ended.
+
Wdate-time
C ObjC C++ ObjC++ CPP(warn_date_time) CppReason(CPP_W_DATE_TIME)
Var(cpp_warn_date_time) Init(0) Warning
Warn about __TIME__, __DATE__ and __TIMESTAMP__ usage.
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 6a34e9c2ae1..dae7a33fb89 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -9313,6 +9313,16 @@ conv_binds_ref_to_prvalue (conversion *c)
return conv_is_prvalue (next_conversion (c));
}
+/* True iff EXPR represents a (subobject of a) temporary. */
+
+static bool
+expr_represents_temporary_p (tree expr)
+{
+ while (handled_component_p (expr))
+ expr = TREE_OPERAND (expr, 0);
+ return TREE_CODE (expr) == TARGET_EXPR;
+}
+
/* True iff C is a conversion that binds a reference to a temporary.
This is a superset of conv_binds_ref_to_prvalue: here we're also
interested in xvalues. */
@@ -9330,18 +9340,14 @@ conv_binds_ref_to_temporary (conversion *c)
struct Derived : Base {};
const Base& b(Derived{});
where we bind 'b' to the Base subobject of a temporary object of type
- Derived. The subobject is an xvalue; the whole object is a prvalue. */
- if (c->kind != ck_base)
- return false;
- c = next_conversion (c);
- if (c->kind == ck_identity && c->u.expr)
- {
- tree expr = c->u.expr;
- while (handled_component_p (expr))
- expr = TREE_OPERAND (expr, 0);
- if (TREE_CODE (expr) == TARGET_EXPR)
- return true;
- }
+ Derived. The subobject is an xvalue; the whole object is a prvalue.
+
+ The ck_base doesn't have to be present for cases like X{}.m. */
+ if (c->kind == ck_base)
+ c = next_conversion (c);
+ if (c->kind == ck_identity && c->u.expr
+ && expr_represents_temporary_p (c->u.expr))
+ return true;
return false;
}
@@ -13428,6 +13434,121 @@ initialize_reference (tree type, tree expr,
return expr;
}
+/* Helper for maybe_warn_dangling_reference to find a problematic CALL_EXPR
+ that initializes the LHS (and at least one of its arguments represents
+ a temporary, as outlined in maybe_warn_dangling_reference), or NULL_TREE
+ if none found. For instance:
+
+ const S& s = S().self(); // S::self (&TARGET_EXPR <...>)
+ const int& r = (42, f(1)); // f(1)
+ const int& t = b ? f(1) : f(2); // f(1)
+ const int& u = b ? f(1) : f(g); // f(1)
+ const int& v = b ? f(g) : f(2); // f(2)
+ const int& w = b ? f(g) : f(g); // NULL_TREE
+ const int& y = (f(1), 42); // NULL_TREE
+ const int& z = f(f(1)); // f(f(1))
+
+ EXPR is the initializer. */
+
+static tree
+do_warn_dangling_reference (tree expr)
+{
+ STRIP_NOPS (expr);
+ switch (TREE_CODE (expr))
+ {
+ case CALL_EXPR:
+ {
+ tree fndecl = cp_get_callee_fndecl_nofold (expr);
+ if (!fndecl
+ || warning_suppressed_p (fndecl, OPT_Wdangling_reference)
+ || !warning_enabled_at (DECL_SOURCE_LOCATION (fndecl),
+ OPT_Wdangling_reference)
+ /* If the function doesn't return a reference, don't warn. This
+ can be e.g.
+ const int& z = std::min({1, 2, 3, 4, 5, 6, 7});
+ which doesn't dangle: std::min here returns an int. */
+ || !TYPE_REF_P (TREE_TYPE (TREE_TYPE (fndecl))))
Maybe TYPE_REF_OBJ_P? Either way is fine.
+ return NULL_TREE;
+
+ /* Here we're looking to see if any of the arguments is a temporary
+ initializing a reference parameter. */
+ for (int i = 0; i < call_expr_nargs (expr); ++i)
+ {
+ tree arg = CALL_EXPR_ARG (expr, i);
+ /* Check that this argument initializes a reference, except for
+ the argument initializing the object of a member function. */
+ if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)
+ && !TYPE_REF_P (TREE_TYPE (arg)))
+ continue;
+ /* It could also be another call taking a temporary and returning
+ it and initializing this reference parameter. */
+ if (do_warn_dangling_reference (arg))
+ return expr;
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ arg = TREE_OPERAND (arg, 0);
+ if (expr_represents_temporary_p (arg))
+ return expr;
+ /* Don't warn about member function like:
+ std::any a(...);
+ S& s = a.emplace<S>({0}, 0);
+ which constructs a new object and returns a reference to it, but
+ we still want to detect:
+ struct S { const S& self () { return *this; } };
+ const S& s = S().self();
+ where 's' dangles. If we've gotten here, the object this function
+ is invoked on is not a temporary. */
+ if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl))
+ break;
+ }
+ return NULL_TREE;
+ }
+ case COMPOUND_EXPR:
+ return do_warn_dangling_reference (TREE_OPERAND (expr, 1));
+ case COND_EXPR:
+ if (tree t = do_warn_dangling_reference (TREE_OPERAND (expr, 1)))
+ return t;
+ return do_warn_dangling_reference (TREE_OPERAND (expr, 2));
+ case PAREN_EXPR:
+ return do_warn_dangling_reference (TREE_OPERAND (expr, 0));
+ default:
+ return NULL_TREE;
+ }
+}
+
+/* Implement -Wdangling-reference, to detect cases like
+
+ int n = 1;
+ const int& r = std::max(n - 1, n + 1); // r is dangling
+
+ This creates temporaries from the arguments, returns a reference to
+ one of the temporaries, but both temporaries are destroyed at the end
+ of the full expression.
+
+ This works by checking if a reference is initialized with a function
+ that returns a reference, and at least one parameter of the function
+ is a reference that is bound to a temporary. It assumes that such a
+ function actually returns one of its arguments.
+
+ DECL is the reference being initialized, INIT is the initializer. */
+
+static void
+maybe_warn_dangling_reference (const_tree decl, tree init)
+{
+ if (!warn_dangling_reference)
+ return;
+ if (!TYPE_REF_P (TREE_TYPE (decl)))
+ return;
+ if (tree call = do_warn_dangling_reference (init))
+ {
+ auto_diagnostic_group d;
+ if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wdangling_reference,
+ "possibly dangling reference to a temporary"))
+ inform (EXPR_LOCATION (call), "the temporary was destroyed at "
+ "the end of the full expression %qE", call);
+ }
+}
+
/* If *P is an xvalue expression, prevent temporary lifetime extension if it
gets used to initialize a reference. */
@@ -13525,6 +13646,9 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups,
tree type = TREE_TYPE (init);
if (processing_template_decl)
return init;
+
+ maybe_warn_dangling_reference (decl, init);
+
if (TYPE_REF_P (type))
init = extend_ref_init_temps_1 (decl, init, cleanups, cond_guard);
else
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 867096b08c6..40f5bf802c3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -459,7 +459,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
DELETE_EXPR_USE_VEC (in DELETE_EXPR).
- (TREE_CALLS_NEW) (in _EXPR or _REF) (commented-out).
ICS_ELLIPSIS_FLAG (in _CONV)
DECL_INITIALIZED_P (in VAR_DECL)
TYPENAME_IS_CLASS_P (in TYPENAME_TYPE)
@@ -4567,6 +4566,9 @@ get_vec_init_expr (tree t)
When appearing in a CONSTRUCTOR, the expression is an unconverted
compound literal.
+ When appearing in a CALL_EXPR, it means that it is a call to
+ a constructor.
+
When appearing in a FIELD_DECL, it means that this field
has been duly initialized in its constructor. */
#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4 (NODE))
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index ab6979bcc50..54fac880d8c 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11246,6 +11246,16 @@ check_return_expr (tree retval, bool *no_warning)
if (processing_template_decl)
return saved_retval;
+ /* A naive attempt to reduce the number of -Wdangling-reference false
+ positives: if we know that this function can return a variable with
+ static storage duration rather than one of its parameters, suppress
+ the warning. */
+ if (warn_dangling_reference
+ && TYPE_REF_P (functype)
+ && bare_retval
You probably need to check VAR_P (bare_retval) here, static_flag is used
for various other things in other tree codes.
+ && TREE_STATIC (bare_retval))
+ suppress_warning (current_function_decl, OPT_Wdangling_reference);
+
/* Actually copy the value returned into the appropriate location. */
if (retval && retval != result)
retval = cp_build_init_expr (result, retval);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 64f77e8367a..0a3a174cdc8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -249,7 +249,8 @@ in the following sections.
-Wno-class-conversion -Wclass-memaccess @gol
-Wcomma-subscript -Wconditionally-supported @gol
-Wno-conversion-null -Wctad-maybe-unsupported @gol
--Wctor-dtor-privacy -Wno-delete-incomplete @gol
+-Wctor-dtor-privacy -Wdangling-reference @gol
+-Wno-delete-incomplete @gol
-Wdelete-non-virtual-dtor -Wno-deprecated-array-compare @gol
-Wdeprecated-copy -Wdeprecated-copy-dtor @gol
-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion
@gol
@@ -3627,6 +3628,42 @@ public static member functions. Also warn if there are
no non-private
methods, and there's at least one private member function that isn't
a constructor or destructor.
+@item -Wdangling-reference @r{(C++ and Objective-C++ only)}
+@opindex Wdangling-reference
+@opindex Wno-dangling-reference
+Warn when a reference is bound to a temporary whose lifetime has ended.
+For example:
+
+@smallexample
+int n = 1;
+const int& r = std::max(n - 1, n + 1); // r is dangling
+@end smallexample
+
+In the example above, two temporaries are created, one for each
+argument, and a reference to one of the temporaries is returned.
+However, both temporaries are destroyed at the end of the full
+expression, so the reference @code{r} is dangling. This warning
+also detects dangling references in member initializer lists:
+
+@smallexample
+const int& f(const int& i) @{ return i; @}
+struct S @{
+ const int &r; // r is dangling
+ S() : r(f(10)) @{ @}
+@};
+@end smallexample
+
+Member functions are checked as well, but only their object argument:
+
+@smallexample
+struct S @{
+ const S& self () @{ return *this; @}
+@};
+const S& s = S().self(); // s is dangling
+@end smallexample
+
+This warning is enabled by @option{-Wextra}.
It would be good to mention using #pragma to disable the warning around
safe functions.
@item -Wdelete-non-virtual-dtor @r{(C++ and Objective-C++ only)}
@opindex Wdelete-non-virtual-dtor
@opindex Wno-delete-non-virtual-dtor
@@ -5936,6 +5973,7 @@ name is still supported, but the newer name is more
descriptive.)
@gccoptlist{-Wclobbered @gol
-Wcast-function-type @gol
+-Wdangling-reference @r{(C++ only)} @gol
-Wdeprecated-copy @r{(C++ only)} @gol
-Wempty-body @gol
-Wenum-conversion @r{(C only)} @gol
diff --git a/gcc/testsuite/g++.dg/cpp23/elision4.C
b/gcc/testsuite/g++.dg/cpp23/elision4.C
index c19b86b8b5f..d39053ad741 100644
--- a/gcc/testsuite/g++.dg/cpp23/elision4.C
+++ b/gcc/testsuite/g++.dg/cpp23/elision4.C
@@ -1,5 +1,6 @@
// PR c++/101165 - P2266R1 - Simpler implicit move
// { dg-do compile { target c++23 } }
+// { dg-options "-Wdangling-reference" }
// Test from P2266R1, $ 5.2. LibreOffice OString constructor.
struct X {
@@ -33,6 +34,6 @@ T& temporary2(T&& x) { return static_cast<T&>(x); }
void
test ()
{
- int& r1 = temporary1 (42);
- int& r2 = temporary2 (42);
+ int& r1 = temporary1 (42); // { dg-warning "dangling reference" }
+ int& r2 = temporary2 (42); // { dg-warning "dangling reference" }
}
diff --git a/gcc/testsuite/g++.dg/cpp23/elision7.C
b/gcc/testsuite/g++.dg/cpp23/elision7.C
index 19fa89ae133..0045842b34f 100644
--- a/gcc/testsuite/g++.dg/cpp23/elision7.C
+++ b/gcc/testsuite/g++.dg/cpp23/elision7.C
@@ -1,5 +1,6 @@
// PR c++/101165 - P2266R1 - Simpler implicit move
// { dg-do compile { target c++23 } }
+// { dg-options "-Wdangling-reference" }
struct X {
X ();
@@ -68,5 +69,5 @@ f7 (T &&t)
void
do_f7 ()
{
- const int &x = f7 (0);
+ const int &x = f7 (0); // { dg-warning "dangling reference" }
}
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference1.C
b/gcc/testsuite/g++.dg/warn/Wdangling-reference1.C
new file mode 100644
index 00000000000..97c81ee716c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference1.C
@@ -0,0 +1,144 @@
+// PR c++/106393
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdangling-reference" }
+
+const int& f(const int& i) { return i; }
+const int& f_(const int& i) { return i; }
+const int& h(int);
+const int& rp(const int *);
+int g;
+const int& globref(const int&) { return g; }
+struct X {
+ int* i;
+ operator const int&() const { return *i; }
+};
+X x{&g};
+
+const int& r1 = f(10); // { dg-warning "dangling reference" }
+// r2 = _ZGR2r2_ = (int) *f ((const int &) &TARGET_EXPR <D.2429, 10>) + 1; (const int
&) &_ZGR2r2_
+const int& r2 = f(10) + 1;
+// Don't warn here, we have
+// r3 = f (X::operator const int& (&x))
+const int& r3 = f(x);
+// Don't warn here, because we've seen the definition of globref
+// and could figure out that it may not return one of its parms.
+// Questionable -- it can also hide bugs --, but it helps here.
+const int& r4 = globref(1);
+const int& r5 = (42, f(10)); // { dg-warning "dangling reference" }
+const int& r6 = (f(10), 42);
+const int& r7 = (f(10)); // { dg-warning "dangling reference" }
+const int& r8 = g ? f(10) : f(9); // { dg-warning "dangling reference" }
+const int& r9 = (42, g ? f(10) : f(9)); // { dg-warning "dangling reference" }
+const int& r10 = (g ? f(10) : f(9), 42);
+// Binds to a reference temporary for r11. No dangling reference.
+const int& r11 = g ? f(10) : 9;
+const int& r12 = g ? 9 : f(10);
+// r12 = f (f ((const int &) &TARGET_EXPR <D.2459, 1>))
+const int& r13 = f(f(1)); // { dg-warning "dangling reference" }
+const int& r14 = f(f_(1)); // { dg-warning "dangling reference" }
+const int& r15 = f(g ? f(1) : f(2)); // { dg-warning "dangling reference" }
+const int& r16 = f(*&f(1)); // { dg-warning "dangling reference" }
+const int& r17 = rp(&f(1));
+const int& r18 = rp(&f(g));
+const int& r19 = h(f(1));
+// Other forms of initializers.
+const int& r20(f(10)); // { dg-warning "dangling reference" }
+const int& r21(f(10)); // { dg-warning "dangling reference" }
+// Returns a ref, but doesn't have a parameter of reference type.
+const int& r22 = h(10);
+const int& r23 = g ? h(10) : f(10); // { dg-warning "dangling reference" }
+const int& r24 = g ? f(10) : h(10); // { dg-warning "dangling reference" }
+const int& r25 = g ? h(10) : (1, f(10)); // { dg-warning "dangling reference" }
+const int& r26 = g ? (1, f(10)) : h(10); // { dg-warning "dangling reference" }
+const int& r29 = f((f_(1), 1)); // { dg-warning "dangling reference" }
+const int& r30 = f((f_(1), g));
+
+struct Z {
+ operator int() { return 42; }
+};
+
+const int& r27 = f(Z()); // { dg-warning "dangling reference" }
+const int& r28 = f(true ? Z() : Z()); // { dg-warning "dangling reference" }
+
+const int& operator|(const int &, Z);
+const int& r31 = 1 | Z(); // { dg-warning "dangling reference" }
+
+// OK: the reference is bound to the 10 so still valid at the point
+// where it's copied into i1.
+int i1 = f(10);
+
+int
+test1 ()
+{
+ const int &lr = f(10); // { dg-warning "dangling reference" }
+ int i2 = f(10);
+ return lr;
+}
+
+struct B { };
+struct D : B { };
+struct C {
+ D d;
+};
+
+C c;
+D d;
+
+using U = D[3];
+
+const B& frotz(const D&);
+const B& b1 = frotz(C{}.d); // { dg-warning "dangling reference" }
+const B& b2 = frotz(D{}); // { dg-warning "dangling reference" }
+const B& b3 = frotz(c.d);
+const B& b4 = frotz(d);
+const B& b5 = frotz(U{}[0]); // { dg-warning "dangling reference" }
+
+// Try returning a subobject.
+const B& bar (const D& d) { return d; }
+const B& b6 = bar (D{}); // { dg-warning "dangling reference" }
+const B& baz (const C& c) { return c.d; }
+const B& b7 = baz (C{}); // { dg-warning "dangling reference" }
+const D& qux (const C& c) { return c.d; }
+const D& d1 = qux (C{}); // { dg-warning "dangling reference" }
+
+struct E {
+ E(int);
+};
+const E& operator*(const E&);
+const E& b8 = *E(1); // { dg-warning "dangling reference" }
+
+struct F : virtual B { };
+struct G : virtual B { };
+struct H : F, G { };
+const B& yum (const F& f) { return f; }
+const B& b9 = yum (F{}); // { dg-warning "dangling reference" }
+const B& lox (const H& h) { return h; }
+const B& b10 = lox (H{}); // { dg-warning "dangling reference" }
+
+struct S {
+ const int &r; // { dg-warning "dangling reference" }
+ S() : r(f(10)) { } // { dg-message "destroyed" }
+};
+
+// From cppreference.
+template<class T>
+const T& max(const T& a, const T& b)
+{
+ return (a < b) ? b : a;
+}
+
+int n = 1;
+const int& refmax = max(n - 1, n + 1); // { dg-warning "dangling reference" }
+
+struct Y {
+ operator int&();
+ operator int&&();
+ const int& foo(const int&);
+};
+
+// x1 = Y::operator int&& (&TARGET_EXPR <D.2410, {}>)
+int&& x1 = Y(); // { dg-warning "dangling reference" }
+int&& x2 = Y{}; // { dg-warning "dangling reference" }
+int& x3 = Y(); // { dg-warning "dangling reference" }
+int& x4 = Y{}; // { dg-warning "dangling reference" }
+const int& t1 = Y().foo(10); // { dg-warning "dangling reference" }
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference2.C
b/gcc/testsuite/g++.dg/warn/Wdangling-reference2.C
new file mode 100644
index 00000000000..dafdb43f1b9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference2.C
@@ -0,0 +1,28 @@
+// PR c++/106393
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdangling-reference" }
+
+namespace std {
+struct any {};
+template <typename _ValueType> _ValueType any_cast(any &&);
+template <typename _Tp> struct remove_reference { using type = _Tp; };
+template <typename _Tp> _Tp forward(typename remove_reference<_Tp>::type);
+template <typename _Tp> typename remove_reference<_Tp>::type move(_Tp);
+} // namespace std
+
+const int &r = std::any_cast<int&>(std::any()); // { dg-warning "dangling
reference" }
+
+template <class T> struct C {
+ T t_; // { dg-warning "dangling reference" }
+ C(T);
+ template <class U> C(U c) : t_(std::forward<T>(c.t_)) {}
+};
+struct A {};
+struct B {
+ B(A);
+};
+int main() {
+ A a;
+ C<A> ca(a);
+ C<B &&>(std::move(ca));
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference3.C
b/gcc/testsuite/g++.dg/warn/Wdangling-reference3.C
new file mode 100644
index 00000000000..4bc20c13b3f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference3.C
@@ -0,0 +1,24 @@
+// PR c++/106393
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wdangling-reference" }
+
+struct A {
+ int ar[4];
+ int& operator[](int i) { return ar[i]; }
+};
+const int &r = A()[2]; // { dg-warning "dangling reference" }
+
+struct S {
+ const S& self () { return *this; }
+};
+const S& s = S().self(); // { dg-warning "dangling reference" }
+
+struct G {
+ const G& g() { return *this; }
+};
+
+struct F {
+ G& f();
+};
+
+const G& g = F().f().g(); // { dg-warning "dangling reference" }
diff --git a/libstdc++-v3/include/bits/locale_classes.tcc
b/libstdc++-v3/include/bits/locale_classes.tcc
index 64cd7534dc6..9cc4f238ee7 100644
--- a/libstdc++-v3/include/bits/locale_classes.tcc
+++ b/libstdc++-v3/include/bits/locale_classes.tcc
@@ -127,6 +127,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @return Reference to facet of type Facet.
* @throw std::bad_cast if @p __loc doesn't contain a facet of type
_Facet.
*/
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-reference"
template<typename _Facet>
const _Facet&
use_facet(const locale& __loc)
@@ -141,6 +143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return static_cast<const _Facet&>(*__facets[__i]);
#endif
}
+#pragma GCC diagnostic pop
// Generic version does nothing.
base-commit: a87819b8f1b890d36a3f05bd9de80be20e9525dd