https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115361

            Bug ID: 115361
           Summary: "possibly dangling reference to a temporary" when
                    object is_empty
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: arthur.j.odwyer at gmail dot com
  Target Milestone: ---

Bug #115229 is related.

// https://godbolt.org/z/os63oEEax

struct GetKey {
          int& rn(      int& x) const { return  x; }
    const int& rc(const int& x) const { return  x; }
          int* pn(      int& x) const { return &x; }
    const int* pc(const int& x) const { return &x; }

    static       int& srn(      int& x) { return  x; }
    static const int& src(const int& x) { return  x; }
};

int f0(int  t) { const int& x = GetKey().rn(t); return x; } // False positive
int f1(int  t) {       int& x = GetKey().rn(t); return x; } // OK
int f2(int  t) { const int& x = GetKey().rc(t); return x; } // False positive
int f3(char c) { const int& x = GetKey().rc(c); return x; } // True(*) positive
int f4(int  t) { const int* x = GetKey().pn(t); return *x; } // OK (compare f0)
int f5(int  t) {       int* x = GetKey().pn(t); return *x; } // OK
int f6(int  t) { const int* x = GetKey().pc(t); return *x; } // OK (compare f2)
int f7(char c) { const int* x = GetKey().pc(c); return *x; } // False negative
(compare f3)
int f8(int  t) { const int& x = GetKey().srn(t); return x; } // OK (compare f0)
int f9(char c) { const int& x = GetKey().src(c); return x; } // True(*)
positive (compare f3)

// f9 in 14.1 is a true(*) positive; it had been a false negative in 13.1.
// f7 in 14.1 gives -Wuninitialized on the use of `x`, but fails to give
-Wdangling-reference!


GCC 14.1 complains---

<source>:12:29: warning: possibly dangling reference to a temporary
[-Wdangling-reference]
   12 | int f0(int  t) { const int& x = GetKey().rn(t); return x; } // False
positive
      |                             ^
<source>:12:44: note: the temporary was destroyed at the end of the full
expression 'GetKey().GetKey::rn(t)'
   12 | int f0(int  t) { const int& x = GetKey().rn(t); return x; } // False
positive
      | 

I notice that GCC 13.1 includes -Wdangling-reference in -Wall, but then 13.2
removed it from -Wall again, presumably because of all the false positives. But
then in 14.1 it's back in -Wall causing trouble again! IMO it doesn't even
belong in -Wextra right now; but I see how you have to ship it in order to find
out how it works in practice. (Still, the answer is "it doesn't work great.")

The warning message could be improved immediately by changing the phrase "the
temporary was destroyed" to "a temporary of type 'GetKey' was destroyed". And
then change "reference *to* a temporary" to "reference *into* a temporary,"
since obviously we have no reference to the 'GetKey' temporary anywhere in this
code.

Another reason to mention the type of the temporary is that the naïve
programmer (me!) might assume that the compiler was giving the warning because
of cases like `f3`, which legitimately does dangle; but then it makes no sense
that `f7` is accepted quietly! The solution to that puzzle is that GCC doesn't
care about the *actual dangling* in `f3` at all; the warning is essentially a
false positive about the temporary of type `GetKey`; GCC doesn't even realize
that we have a temporary of type `int`! By mentioning the type of the
temporary, you'll help the programmer build the correct mental model (of what
the compiler is diagnosing) more quickly, with less trial-and-error.

---

The big problem with this diagnostic, for programmers, is that it's so
encumbered with heuristics that it's not easily actionable. My coworkers
discovered by trial and error that you can get around it by replacing our
code's

    const int& x = GetKey()(t);

with

    static constexpr GetKey g;
    const int& x = g(t);

(here `GetKey` is a template policy parameter, basically something like
`std::hash<int>`, with an `operator()` — which also means that we cannot work
around the problem by making the function `static`, as shown in `src` above).
But this is dumb. Another more generic solution — proposed only tongue-in-cheek
— was

    // https://godbolt.org/z/ezE9KjKnP
    const int& x = (+[](const int& z) -> decltype(auto) { return GetKey()(z);
})(t);

---

Since the heuristic is concerned only with dangling references to *members of
the GetKey object itself*, could you maybe add yet another heuristic to
suppress the warning whenever the GetKey object is_empty?
  • [Bug c++/115361] New: "... arthur.j.odwyer at gmail dot com via Gcc-bugs

Reply via email to