public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call
@ 2021-07-21 18:07 weilercdale at gmail dot com
  2021-07-21 18:18 ` [Bug c++/101566] " pinskia at gcc dot gnu.org
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: weilercdale at gmail dot com @ 2021-07-21 18:07 UTC (permalink / raw)
  To: gcc-bugs

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

            Bug ID: 101566
           Summary: gcc miscompiles lambda used as tuple-like object
                    applied to function for call
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: weilercdale at gmail dot com
  Target Milestone: ---

Created attachment 51191
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=51191&action=edit
minimized test case

The following code uses a compiler generated structure via a lambda and `[=]`
capture-list to create a "tuple"-like object, `get<N>` is implemented in terms
of the gcc provided intrinsic `__integer_pack(N)` to emulate
`std::index_sequence` for peeling off the value from the "tuple"-like object.
`apply` calls any callable by applying `get<>` to each value in the
"tuple"-like object. `PackCount` exposes the parameter pack count of the
"tuple"-like object created with `make`, A `decltype` of the lambda is used to
create a new struct `Lambda` that inherits both, making
`PackCount<Ts...>::ELEMENTS` available as a compile-time constant to each
created "tuple"-like object. A `Tuple` type is provided by taking the
`decltype` of `make` synthesizing values for the call with `declval`. The code
compiles in gcc, msvc, and clang but only works in the latter two compilers,
gcc appears to miscompile the code under -O3. When compiling in gcc with
-fsanitize=undefined, the code isn't miscompiled, and it runs fine. Under other
optimization levels the code isn't miscompiled either. This appears to affect
multiple versions of gcc including 10.1, 10.2, 10.3, 11.1.0, 11.1.1, and
current trunk as can be verified on compiler explorer here
https://godbolt.org/z/3sYnrj6nW

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
@ 2021-07-21 18:18 ` pinskia at gcc dot gnu.org
  2021-07-21 18:24 ` weilercdale at gmail dot com
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: pinskia at gcc dot gnu.org @ 2021-07-21 18:18 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
  f.0_1 = f_8(D);
  tuple = t;
  _11 = &tuple.__ts#2;
  tuple ={v} {CLOBBER};


template<Size N, typename T>
constexpr decltype(auto) get(T tuple) { return *tuple(Get<N>{}); }


I think the above function (get) is broken and is returning a reference to the
argument and that would be invalid.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
  2021-07-21 18:18 ` [Bug c++/101566] " pinskia at gcc dot gnu.org
@ 2021-07-21 18:24 ` weilercdale at gmail dot com
  2021-07-21 18:34 ` pinskia at gcc dot gnu.org
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: weilercdale at gmail dot com @ 2021-07-21 18:24 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #2 from Dale Weiler <weilercdale at gmail dot com> ---
(In reply to Andrew Pinski from comment #1)
>   f.0_1 = f_8(D);
>   tuple = t;
>   _11 = &tuple.__ts#2;
>   tuple ={v} {CLOBBER};
> 
> 
> template<Size N, typename T>
> constexpr decltype(auto) get(T tuple) { return *tuple(Get<N>{}); }
> 
> 
> I think the above function (get) is broken and is returning a reference to
> the argument and that would be invalid.

Ah, passing `T&` here instead of T does appear to avoid the issue, the question
now becomes why does -fsanitize=undefined find nothing, and is the return type,
i.e `declval(get<N>(t))` different in gcc compared to the other compiles. I
would expect similar issues in the other compilers if this is returning a
reference to the temporary given by the argument list of `get`

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
  2021-07-21 18:18 ` [Bug c++/101566] " pinskia at gcc dot gnu.org
  2021-07-21 18:24 ` weilercdale at gmail dot com
@ 2021-07-21 18:34 ` pinskia at gcc dot gnu.org
  2021-07-21 18:42 ` weilercdale at gmail dot com
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: pinskia at gcc dot gnu.org @ 2021-07-21 18:34 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #3 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
(In reply to Dale Weiler from comment #2) 
> Ah, passing `T&` here instead of T does appear to avoid the issue, the
> question now becomes why does -fsanitize=undefined find nothing, and is the
> return type, i.e `declval(get<N>(t))` different in gcc compared to the other
> compiles. I would expect similar issues in the other compilers if this is
> returning a reference to the temporary given by the argument list of `get`

So -fsanitize=undefined adds some extra code which just happens to cause GCC
not to optimize as much.

Try -fsanitize=address instead which enables -fsanitize-address-use-after-scope
which should detect the problen but I see it does not ....

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
                   ` (2 preceding siblings ...)
  2021-07-21 18:34 ` pinskia at gcc dot gnu.org
@ 2021-07-21 18:42 ` weilercdale at gmail dot com
  2021-07-21 18:51 ` weilercdale at gmail dot com
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: weilercdale at gmail dot com @ 2021-07-21 18:42 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #4 from Dale Weiler <weilercdale at gmail dot com> ---
(In reply to Andrew Pinski from comment #3)
> (In reply to Dale Weiler from comment #2) 
> > Ah, passing `T&` here instead of T does appear to avoid the issue, the
> > question now becomes why does -fsanitize=undefined find nothing, and is the
> > return type, i.e `declval(get<N>(t))` different in gcc compared to the other
> > compiles. I would expect similar issues in the other compilers if this is
> > returning a reference to the temporary given by the argument list of `get`
> 
> So -fsanitize=undefined adds some extra code which just happens to cause GCC
> not to optimize as much.
> 
> Try -fsanitize=address instead which enables
> -fsanitize-address-use-after-scope which should detect the problem but I see
> it does not ....

I'm unsure why this would be a temporary, given:

auto lambda = [=](auto&& f) mutable -> decltype(auto) { return f(&ts...); };

This should capture all `ts...` by copy, the `f(&ts...)` should reference the
captures of `this` inside the lambda (of the compiler generated structure.)

When `get` is called, the values from the lambda are returned by dereferencing
since the function is called with pointers (synthesized with `&ts...` in the
lambda). I verified this by printing `typeid(decltype(get<N>(t))).name()`
passed to `__cxa_demangle`. There isn't any reference types, just to be sure
that's not some runtime quirk, the compile-time result of `std::is_same_v<int,
decltype(get<0>)>` is true.

Where is the temporary happening?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
                   ` (3 preceding siblings ...)
  2021-07-21 18:42 ` weilercdale at gmail dot com
@ 2021-07-21 18:51 ` weilercdale at gmail dot com
  2021-07-21 19:22 ` weilercdale at gmail dot com
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: weilercdale at gmail dot com @ 2021-07-21 18:51 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #5 from Dale Weiler <weilercdale at gmail dot com> ---
(In reply to Dale Weiler from comment #4)
> (In reply to Andrew Pinski from comment #3)
> > (In reply to Dale Weiler from comment #2) 
> > > Ah, passing `T&` here instead of T does appear to avoid the issue, the
> > > question now becomes why does -fsanitize=undefined find nothing, and is the
> > > return type, i.e `declval(get<N>(t))` different in gcc compared to the other
> > > compiles. I would expect similar issues in the other compilers if this is
> > > returning a reference to the temporary given by the argument list of `get`
> > 
> > So -fsanitize=undefined adds some extra code which just happens to cause GCC
> > not to optimize as much.
> > 
> > Try -fsanitize=address instead which enables
> > -fsanitize-address-use-after-scope which should detect the problem but I see
> > it does not ....
> 
> I'm unsure why this would be a temporary, given:
> 
> auto lambda = [=](auto&& f) mutable -> decltype(auto) { return f(&ts...); };
> 
> This should capture all `ts...` by copy, the `f(&ts...)` should reference
> the captures of `this` inside the lambda (of the compiler generated
> structure.)
> 
> When `get` is called, the values from the lambda are returned by
> dereferencing since the function is called with pointers (synthesized with
> `&ts...` in the lambda). I verified this by printing
> `typeid(decltype(get<N>(t))).name()` passed to `__cxa_demangle`. There isn't
> any reference types, just to be sure that's not some runtime quirk, the
> compile-time result of `std::is_same_v<int, decltype(get<0>)>` is true.
> 
> Where is the temporary happening?

This is curious, omitting `decltype(auto)` for get, as in just `auto` seems to
work around the issue as well.

template<Size N, typename T>
constexpr auto get(T tuple) { return *tuple(Get<N>{}); }

I'm a bit out of my element here, in that I don't understand the semantic
differences between the two and if that behavior is correct.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
                   ` (4 preceding siblings ...)
  2021-07-21 18:51 ` weilercdale at gmail dot com
@ 2021-07-21 19:22 ` weilercdale at gmail dot com
  2021-07-21 19:23 ` weilercdale at gmail dot com
  2021-07-21 19:56 ` redi at gcc dot gnu.org
  7 siblings, 0 replies; 9+ messages in thread
From: weilercdale at gmail dot com @ 2021-07-21 19:22 UTC (permalink / raw)
  To: gcc-bugs

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

Dale Weiler <weilercdale at gmail dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         Resolution|---                         |INVALID
             Status|UNCONFIRMED                 |RESOLVED

--- Comment #6 from Dale Weiler <weilercdale at gmail dot com> ---
(In reply to Dale Weiler from comment #4)
> (In reply to Andrew Pinski from comment #3)
> > (In reply to Dale Weiler from comment #2) 
> > > Ah, passing `T&` here instead of T does appear to avoid the issue, the
> > > question now becomes why does -fsanitize=undefined find nothing, and is the
> > > return type, i.e `declval(get<N>(t))` different in gcc compared to the other
> > > compiles. I would expect similar issues in the other compilers if this is
> > > returning a reference to the temporary given by the argument list of `get`
> > 
> > So -fsanitize=undefined adds some extra code which just happens to cause GCC
> > not to optimize as much.
> > 
> > Try -fsanitize=address instead which enables
> > -fsanitize-address-use-after-scope which should detect the problem but I see
> > it does not ....
> 
> I'm unsure why this would be a temporary, given:
> 
> auto lambda = [=](auto&& f) mutable -> decltype(auto) { return f(&ts...); };
> 
> This should capture all `ts...` by copy, the `f(&ts...)` should reference
> the captures of `this` inside the lambda (of the compiler generated
> structure.)
> 
> When `get` is called, the values from the lambda are returned by
> dereferencing since the function is called with pointers (synthesized with
> `&ts...` in the lambda). I verified this by printing
> `typeid(decltype(get<N>(t))).name()` passed to `__cxa_demangle`. There isn't
> any reference types, just to be sure that's not some runtime quirk, the
> compile-time result of `std::is_same_v<int, decltype(get<0>)>` is true.
> 
> Where is the temporary happening?

This is curious, omitting `decltype(auto)` for get, as in just `auto` seems to
work around the issue as well.

template<Size N, typename T>
constexpr auto get(T tuple) { return *tuple(Get<N>{}); }

I'm a bit out of my element here, in that I don't understand the semantic
differences between the two and if that behavior is correct.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
                   ` (5 preceding siblings ...)
  2021-07-21 19:22 ` weilercdale at gmail dot com
@ 2021-07-21 19:23 ` weilercdale at gmail dot com
  2021-07-21 19:56 ` redi at gcc dot gnu.org
  7 siblings, 0 replies; 9+ messages in thread
From: weilercdale at gmail dot com @ 2021-07-21 19:23 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #7 from Dale Weiler <weilercdale at gmail dot com> ---
Yeah the code example is invalid, there is a reference to a temporary,
decltype(auto) on *ptr produces reference type, somehow I thought it produced
the value type, sorry for the confusion.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101566] gcc miscompiles lambda used as tuple-like object applied to function for call
  2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
                   ` (6 preceding siblings ...)
  2021-07-21 19:23 ` weilercdale at gmail dot com
@ 2021-07-21 19:56 ` redi at gcc dot gnu.org
  7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2021-07-21 19:56 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #8 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Dale Weiler from comment #5)
> This is curious, omitting `decltype(auto)` for get, as in just `auto` seems
> to work around the issue as well.
> 
> template<Size N, typename T>
> constexpr auto get(T tuple) { return *tuple(Get<N>{}); }
> 
> I'm a bit out of my element here, in that I don't understand the semantic
> differences between the two and if that behavior is correct.

auto makes it return by value, decltype(auto) returns exactly the type of the
return statement (so if it's a reference, so is the return type).

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-07-21 19:56 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-21 18:07 [Bug c++/101566] New: gcc miscompiles lambda used as tuple-like object applied to function for call weilercdale at gmail dot com
2021-07-21 18:18 ` [Bug c++/101566] " pinskia at gcc dot gnu.org
2021-07-21 18:24 ` weilercdale at gmail dot com
2021-07-21 18:34 ` pinskia at gcc dot gnu.org
2021-07-21 18:42 ` weilercdale at gmail dot com
2021-07-21 18:51 ` weilercdale at gmail dot com
2021-07-21 19:22 ` weilercdale at gmail dot com
2021-07-21 19:23 ` weilercdale at gmail dot com
2021-07-21 19:56 ` redi at gcc dot gnu.org

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).