[Bug libstdc++/97600] [ranges] result of static assertion depends on unrelated statement

2020-10-27 Thread ppalka at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97600

--- Comment #2 from Patrick Palka  ---
I think all views whose begin()/end() has a placeholder return type and
performs direct initialization of a _Iterator/_Sentinel from *this are affected
by some variant of this issue.

A library-side workaround would be to templatize view_interface::operator
bool() like so:

diff --git a/libstdc++-v3/include/bits/ranges_util.h
b/libstdc++-v3/include/bits/ranges_util.h
index cc50e2ad4e4..8563a5dfcb0 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -86,13 +86,15 @@ namespace ranges
   empty() const requires forward_range
   { return ranges::begin(_M_derived()) == ranges::end(_M_derived()); }

-  constexpr explicit
-  operator bool() requires requires { ranges::empty(_M_derived()); }
-  { return !ranges::empty(_M_derived()); }
-
-  constexpr explicit
-  operator bool() const requires requires { ranges::empty(_M_derived()); }
-  { return !ranges::empty(_M_derived()); }
+  template _Tp>
+   constexpr explicit
+   operator _Tp() requires requires { ranges::empty(_M_derived()); }
+   { return !ranges::empty(_M_derived()); }
+
+  template _Tp>
+   constexpr explicit
+   operator _Tp() const requires requires { ranges::empty(_M_derived()); }
+   { return !ranges::empty(_M_derived()); }

   constexpr auto
   data() requires contiguous_iterator>

So that when we considering this conversion function in step 5, we deduce
_Tp=_Iterator and short circuit satisfaction of the conversion function's
associated constraints in step 6.

[Bug libstdc++/97600] [ranges] result of static assertion depends on unrelated statement

2020-10-27 Thread ppalka at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97600

Patrick Palka  changed:

   What|Removed |Added

  Known to fail||10.2.0, 11.0

--- Comment #1 from Patrick Palka  ---
This happens because of the following chain of events:

1. Calling basic_istream_view::begin() entails instantiating its definition to
perform return type deduction.
2. We perform overload resolution of the direct-initialization _Iterator{*this}
inside its definition.
3. As part of overload resolution, we consider the copy ctor _Iterator(const
_Iterator&).
4. We try to find an implicit conversion sequence from basic_istream_view to
const _Iterator&.
5. We consider a conversion sequence that goes through the conversion function
view_interface::operator bool().
6. We check the conversion function's constraint 'requires {
ranges::empty(_M_derived()); }'.
7. ranges::empty() checks that view_interface::empty() is callable and
therefore has satisfied constraints
8. view_interface::empty() requires range, which requires
that basic_istream_view::begin() is callable, which requires that we deduce its
return type.  But we're already in the the middle of deducing it, so we hit a
SFINAE error, which causes satisfaction of range to fail.
9. We cache this negative satisfaction result for (the atomic constraint of)
range.
10. We rule out the copy ctor during overload resolution and eventually resolve
the direct-initialization in the expected way.

When we later evaluate the static assert, we just return the cached
satisfaction result.

I have an experimental patch here
https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557117.html that makes
us rule out the conversion function in step 5 sooner without first checking its
constraints, which would be one way to fix this.