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