public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const
@ 2020-06-26 12:37 rene.rahn@fu-berlin.de
  2020-06-26 19:25 ` [Bug c++/95910] " ppalka at gcc dot gnu.org
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: rene.rahn@fu-berlin.de @ 2020-06-26 12:37 UTC (permalink / raw)
  To: gcc-bugs

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

            Bug ID: 95910
           Summary: transform view in combination with single view calls
                    const qualified begin even if it is not const
           Product: gcc
           Version: 10.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: rene.rahn@fu-berlin.de
  Target Milestone: ---

Hi gcc team,

I am wondering why the following code results in an static assert:
```cpp
#include <concepts>
#include <ranges>
#include <vector>

auto transform_v = [] (auto && v)
{
    static_assert(std::same_as<decltype(v), int &>);
    return v;
};

using range_t = decltype(std::views::single(0));  // falsely calls const
qualified begin somewhere in stack
// using range_t = std::vector<int>; // OK, everyting as expected.
using transformed_view_t = decltype(std::declval<range_t &>() |
std::views::transform(transform_v));
using ref_t = std::ranges::range_reference_t<transformed_view_t>;
```

Here is the example on https://godbolt.org/z/QWvYue.

It seems, that somewhere in the stack the transform view is promoted to a const
view even though it should not, shouldn't it?

Sorry, if I got something wrong.

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

* [Bug c++/95910] transform view in combination with single view calls const qualified begin even if it is not const
  2020-06-26 12:37 [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const rene.rahn@fu-berlin.de
@ 2020-06-26 19:25 ` ppalka at gcc dot gnu.org
  2020-07-02 11:00 ` rene.rahn@fu-berlin.de
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: ppalka at gcc dot gnu.org @ 2020-06-26 19:25 UTC (permalink / raw)
  To: gcc-bugs

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

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ppalka at gcc dot gnu.org

--- Comment #1 from Patrick Palka <ppalka at gcc dot gnu.org> ---
Thanks for the report.

The const-qualified transform_view::begin() member has as its constraints

      constexpr _Iterator<true>
      begin() const
        requires range<const _Vp>
          && regular_invocable<const _Fp&, range_reference_t<const _Vp>>.

So as part of overload resolution of the implicit call to
'transform_view::begin', we end up evaluating 'is_invocable_v<lambda, const
int&>'.  But because the lambda's return type is omitted and because evaluation
of 'is_invocable_v' requires knowing the return type, we have to instantiate
the body of the lambda in order to deduce its return type.  And instantiation
leads to a hard error from the failed static_assert.

So noe workaround here is to give the lambda an explicit return type.

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

* [Bug c++/95910] transform view in combination with single view calls const qualified begin even if it is not const
  2020-06-26 12:37 [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const rene.rahn@fu-berlin.de
  2020-06-26 19:25 ` [Bug c++/95910] " ppalka at gcc dot gnu.org
@ 2020-07-02 11:00 ` rene.rahn@fu-berlin.de
  2020-07-08 13:22 ` ppalka at gcc dot gnu.org
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: rene.rahn@fu-berlin.de @ 2020-07-02 11:00 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #2 from Rene Rahn <rene.rahn@fu-berlin.de> ---
Ok, thanks for the explanation. I do understand the issue now and why it causes
the hard error and not an substitution failure. 
But honestly, given that it works for container because they are wrapped in a
ref_view with pointer semantics, makes this very hard to understand. And
basically, if you transform something that calls somewhere in the stack a
function with auto return type you might not be able to even do
`decltype(expression)` to get the return type deduced any more, because the
compiler has to instantiate the expression. 

That makes generic code with auto return types kind of difficult to use, does
it? I mean, especially as a library writer I must make sure that the client can
use my methods/types in these contexts. And it feels plausible that types are
constrained to be mutable somewhere in this context.
Is there a general trick to avoid this, except guaranteeing to know the return
type before the expression is evaluated?

Many thanks for your help!

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

* [Bug c++/95910] transform view in combination with single view calls const qualified begin even if it is not const
  2020-06-26 12:37 [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const rene.rahn@fu-berlin.de
  2020-06-26 19:25 ` [Bug c++/95910] " ppalka at gcc dot gnu.org
  2020-07-02 11:00 ` rene.rahn@fu-berlin.de
@ 2020-07-08 13:22 ` ppalka at gcc dot gnu.org
  2020-07-08 13:55 ` rene.rahn@fu-berlin.de
  2020-07-08 14:31 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: ppalka at gcc dot gnu.org @ 2020-07-08 13:22 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #3 from Patrick Palka <ppalka at gcc dot gnu.org> ---
(In reply to Rene Rahn from comment #2)
> Ok, thanks for the explanation. I do understand the issue now and why it
> causes the hard error and not an substitution failure. 
> But honestly, given that it works for container because they are wrapped in
> a ref_view with pointer semantics, makes this very hard to understand.

Hmm yes, it seems (for the record) the reason this works for std::vector is
because views::transform wraps the type in a ref_view, and ref_view's
const-qualified begin() member returns a non-const iterator.  So both
range_reference_t<_Vp> and range_reference_t<const _Vp> (where _Vp =
ref_view<vector<int>>) resolve to int&.

On the other hand, single_view<T>'s const-qualified begin() member returns a
pointer to const T, so range_reference_t<const _Vp> (with _Vp = single_view)
resolves to const int&.  And so when we go to check the constraints of 
transform_view::begin() const we end up instantiating the lambda with a const
int& argument, leading to a hard error.

> And basically, if you transform something that calls somewhere in the stack a
> function with auto return type you might not be able to even do
> `decltype(expression)` to get the return type deduced any more, because the
> compiler has to instantiate the expression. 
> 
> That makes generic code with auto return types kind of difficult to use,
> does it? I mean, especially as a library writer I must make sure that the
> client can use my methods/types in these contexts. And it feels plausible
> that types are constrained to be mutable somewhere in this context.
> Is there a general trick to avoid this, except guaranteeing to know the
> return type before the expression is evaluated?

Hmm, if you can't easily specify a concrete return type, then you could maybe
try constraining the lambda appropriately.  In this particular example you
could replace the static_assert with an analogous require-clause, which would
turn the hard error into a SFINAE-friendly error.

We've also been bitten by using a deduced auto return type instead of a
concrete return type, in particular in the definitions of the ranges::empty()
and ranges::iter_move() CPOs, see e.g. PR93978 and PR92894. 

> 
> Many thanks for your help!

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

* [Bug c++/95910] transform view in combination with single view calls const qualified begin even if it is not const
  2020-06-26 12:37 [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const rene.rahn@fu-berlin.de
                   ` (2 preceding siblings ...)
  2020-07-08 13:22 ` ppalka at gcc dot gnu.org
@ 2020-07-08 13:55 ` rene.rahn@fu-berlin.de
  2020-07-08 14:31 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: rene.rahn@fu-berlin.de @ 2020-07-08 13:55 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #4 from Rene Rahn <rene.rahn@fu-berlin.de> ---
> Hmm, if you can't easily specify a concrete return type, then you could maybe > try constraining the lambda appropriately.  In this particular example you 
> could replace the static_assert with an analogous require-clause, which would > turn the hard error into a SFINAE-friendly error.

Yes, this is basically what I would do know (AFAIK also recommended to always
constraint generic types). In this case I could work with an explicit return
type, though.

Many thanks for your support and time.

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

* [Bug c++/95910] transform view in combination with single view calls const qualified begin even if it is not const
  2020-06-26 12:37 [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const rene.rahn@fu-berlin.de
                   ` (3 preceding siblings ...)
  2020-07-08 13:55 ` rene.rahn@fu-berlin.de
@ 2020-07-08 14:31 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: ppalka at gcc dot gnu.org @ 2020-07-08 14:31 UTC (permalink / raw)
  To: gcc-bugs

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

Patrick Palka <ppalka at gcc dot gnu.org> changed:

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

--- Comment #5 from Patrick Palka <ppalka at gcc dot gnu.org> ---
No problem, thanks for the report.

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

end of thread, other threads:[~2020-07-08 14:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-26 12:37 [Bug c++/95910] New: transform view in combination with single view calls const qualified begin even if it is not const rene.rahn@fu-berlin.de
2020-06-26 19:25 ` [Bug c++/95910] " ppalka at gcc dot gnu.org
2020-07-02 11:00 ` rene.rahn@fu-berlin.de
2020-07-08 13:22 ` ppalka at gcc dot gnu.org
2020-07-08 13:55 ` rene.rahn@fu-berlin.de
2020-07-08 14:31 ` ppalka 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).