https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121646
Bug ID: 121646
Summary: `Substitution failure` does panic and aborts
everything
Product: gcc
Version: 15.2.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: shyeyian at petalmail dot com
Target Milestone: ---
Example:
```
#include <concepts>
#include <string>
struct my_string
{
private:
char* ptr;
public:
operator std::string_view ( ) const
{
return std::string_view(ptr);
}
auto begin() const
{
return std::string_view(*this).begin();
}
auto end() const
{
return std::string_view(*this).end();
}
};
int main() { }
```
Output (Linux, g++ 15.2.1):
```
[anonymous@matebook anonymous]$ g++ -std=c++26 tmp.cpp -o tmp.o
In file included from
/usr/include/c++/15.2.1/bits/stl_iterator_base_types.h:73,
from /usr/include/c++/15.2.1/bits/stl_construct.h:61,
from /usr/include/c++/15.2.1/bits/char_traits.h:59,
from /usr/include/c++/15.2.1/string:44,
from tmp.cpp:2:
/usr/include/c++/15.2.1/bits/ranges_base.h: In substitution of ‘template<class
_Tp> requires (__maybe_borrowed_range<_Tp>) && ((is_array_v<typename
std::remove_reference<_Tp>::type>) || (__member_begin<_Tp>) ||
(__adl_begin<_Tp>)) constexpr auto
std::ranges::__access::_Begin::operator()(_Tp&&) const [with _Tp = const
my_string&]’:
/usr/include/c++/15.2.1/bits/ranges_base.h:516:15: required by substitution
of ‘template<class _Range, class _DRange> requires !(is_same_v<_DRange,
std::basic_string_view<_CharT, _Traits> >) && (contiguous_range<_Range>) &&
(sized_range<_Range>) && (is_same_v<typename
std::__detail::__iter_traits_impl<typename
std::remove_cvref<decltype(std::ranges::__access::__begin((declval<_Range&>)()))>::type,
std::indirectly_readable_traits<typename
std::remove_cvref<decltype(std::ranges::__access::__begin((declval<_Range&>)()))>::type>
>::__iter_traits<typename
std::remove_cvref<decltype(std::ranges::__access::__begin((declval<_Range&>)()))>::type,
std::indirectly_readable_traits<typename
std::remove_cvref<decltype(std::ranges::__access::__begin((declval<_Range&>)()))>::type>
>::value_type, _CharT>) && !(is_convertible_v<_Range, const _CharT*>) &&
!requires(_DRange& __d) {__d->__conv_op ();} constexpr
std::basic_string_view<char>::basic_string_view(_Range&&) [with _Range = const
my_string&; _DRange = my_string]’
516 | ranges::begin(__t);
| ~~~~~~~~~~~~~^~~~~
tmp.cpp:19:42: required from here
19 | return std::string_view(*this).end();
| ^
/usr/include/c++/15.2.1/bits/iterator_concepts.h:1035:15: required for the
satisfaction of ‘__member_begin<_Tp>’ [with _Tp = const my_string&]
/usr/include/c++/15.2.1/bits/iterator_concepts.h:1035:32: in requirements
with ‘_Tp& __t’ [with _Tp = const my_string&]
/usr/include/c++/15.2.1/bits/iterator_concepts.h:1035:32: error: satisfaction
value of atomic constraint ‘requires(_Tp& __t)
{{std::ranges::__access::__decay_copy(__t->begin())} -> decltype(auto)
[requires std::input_or_output_iterator<<placeholder>, >];} [with _Tp = const
my_string&]’ changed from ‘false’ to ‘true’
1035 | concept __member_begin = requires(_Tp& __t)
| ^~~~~~~~~~~~~~~~~~
1036 | {
| ~
1037 | { __decay_copy(__t.begin()) } -> input_or_output_iterator;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1038 | };
| ~
In file included from /usr/include/c++/15.2.1/string_view:58,
from /usr/include/c++/15.2.1/bits/basic_string.h:51,
from /usr/include/c++/15.2.1/string:56:
/usr/include/c++/15.2.1/bits/ranges_base.h:516:22: note: satisfaction value
first evaluated to ‘false’ from here
516 | ranges::begin(__t);
| ~~~~~~~~~~~~~^~~~~
[anonymous@matebook anonymous]$
```
You see, once we call `std::string_view(*this)`:
- We expect the `my_string::operator std::string_view` constructor here
- But according to the overload resolution, we should try all the candicates
- We try the `std::string_view::string_view(Range&&)` candicate
- We meet a `atomic constraint value changes` error.
- When we meet this error, should we:
- A) only stop this candicate, and still continue to try others?
- B) panic this error globally and aborts all the rest candicates, abort
everything?
I wonder the standard here, as both clang++ and msvc seems to adapt the A) but
gcc seems to adapt the B).