The following code compiles fine on GCC 10, Clang, MSVC, but fails in GCC 11/trunk: ### begin struct foo_tag{}; struct bar_tag{}; template <class T> concept fooable = requires(T it) { invoke_tag(foo_tag{}, it); // <-- here }; template<class T> auto invoke_tag(foo_tag, T in) { return in; } template<fooable T> auto invoke_tag(bar_tag, T it) { return it; } int main() { // Neither line below compiles in GCC 11, independently of the other return invoke_tag(foo_tag{}, 2); return invoke_tag(bar_tag{}, 2); } ### end Produces the following compiler error: ### begin <source>: In substitution of 'template<class T> requires fooable<T> auto invoke_tag(bar_tag, T) [with T = int]': <source>:6:15: required by substitution of 'template<class T> requires fooable<T> auto invoke_tag(bar_tag, T) [with T = int]' <source>:21:35: required from here <source>:5:9: required for the satisfaction of 'fooable<T>' [with T = int] <source>:5:19: in requirements with 'T it' [with T = int] <source>:5:19: error: satisfaction of atomic constraint 'requires(T it) {invoke_tag({}, it);} [with T = T]' depends on itself 5 | concept fooable = requires(T it) { | ^~~~~~~~~~~~~~~~ 6 | invoke_tag(foo_tag{}, it); | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 | }; | ~ ### end It seems that the template requirements of invoke_tag(bar_tag, int) are considered while evaluating line marked "here". Requirements of irrelevant overloads should not be considered, as it can potentially lead to falsely reporting a cyclic dependency. This bug effectively prevents using the modern tag_invoke pattern, which is increasingly used as a customisation point in many modern libraries.
Rejected since r11-2774 c++: Check satisfaction before non-dep convs. [CWG2369] It's very hard to use concepts to protect a template from hard errors due to unwanted instantiation if constraints aren't checked until after doing all substitution and checking of non-dependent conversions. It was pretty straightforward to insert the satisfaction check into the logic, but I needed to make the 3-parameter version of satisfy_declaration_constraints call push_tinst_level like the 2-parameter version already does. For simplicity, I also made it add any needed outer template arguments from the TEMPLATE_DECL to the args. The testsuite changes are mostly because this change causes unsatisfaction to cause deduction to fail rather than reject the candidate later in overload resolution.
(In reply to the_gamester28 from comment #0) > It seems that the template requirements of invoke_tag(bar_tag, int) are > considered while evaluating line marked "here". Requirements of irrelevant > overloads should not be considered, as it can potentially lead to falsely > reporting a cyclic dependency. This is as specified by http://wg21.link/cwg2369 I think it would be reasonable to allow a compiler to accept the testcase under a generalization of 13.9.1/9: "If the function selected by overload resolution (12.4) can be determined without instantiating a class template definition, it is unspecified whether that instantiation actually takes place." But that does not require a compiler to accept it. It might make sense to check non-dependent conversions that don't require template instantiation, then constraints, then non-dependent conversions that do require template instantiation. But that's a matter for the committee; G++ is conforming to the current working paper.
Hi Jason, as you linked to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97704, I'm interested if your suggestion would allow that, too? > But that's a matter for the committee Will such issues be forwarded, or do we need to submit a paper / regression / idea? I wouldn't know how to proceed here and / or what needs to be done. Thank you!
(In reply to Jason Merrill from comment #2) > (In reply to the_gamester28 from comment #0) > > It seems that the template requirements of invoke_tag(bar_tag, int) are > > considered while evaluating line marked "here". Requirements of irrelevant > > overloads should not be considered, as it can potentially lead to falsely > > reporting a cyclic dependency. > > This is as specified by http://wg21.link/cwg2369 > > I think it would be reasonable to allow a compiler to accept the testcase > under a generalization of 13.9.1/9: "If the function selected by overload > resolution (12.4) can be determined without instantiating a class template > definition, it is unspecified whether that instantiation actually takes > place." > > But that does not require a compiler to accept it. > > It might make sense to check non-dependent conversions that don't require > template instantiation, then constraints, then non-dependent conversions > that do require template instantiation. But that's a matter for the > committee; G++ is conforming to the current working paper. Perhaps I was too quick to assert what I expected the implementation details to be. The fooable<T> concept should be satisfied for any (T it) for which invoke_tag(foo_tag{}, it) is unambiguously applicable. The fact that the invoke_tag(bar_tag, T) overload exists should not preclude that. It is not up to me how the compiler comes to that end, but that is the desired outcome. It is still possible to ban recursive constraints, keep the new GCC 11 implementation, and still have this particular code compile with a small alteration in behaviour: The compiler does not immediately bail out as soon as it sees recursion in an overload candidate, but marks that particular candidate as invalid. If there are no applicable overloads (because they are all recursive, or they are invalid for some other reason), then the compiler can reject the code and list all of the reasons why all of the candidates are invalid. In this case, it would mark invoke_tag(bar_tag, int) as not-applicable due to the constraint recursion, and carry on checking the rest of the overloads for applicability, until it is sure that there is one and only one applicable overload: invoke_tag(foo_tag, int).
GCC 11.1 has been released, retargeting bugs to GCC 11.2.
FWIW, one workaround for this kind of constraint looping is to encode the non-dependent conversion as a constraint. For this particular testcase, this would mean changing the signature of template<fooable T> auto invoke_tag(bar_tag, T it); to template<std::convertible_to<bar_tag> T, fooable U> auto invoke_tag(T, U it);
GCC 11.2 is being released, retargeting bugs to GCC 11.3
*** Bug 102065 has been marked as a duplicate of this bug. ***
GCC 11.3 is being released, retargeting bugs to GCC 11.4.
*** Bug 106538 has been marked as a duplicate of this bug. ***
*** Bug 107429 has been marked as a duplicate of this bug. ***
*** Bug 109397 has been marked as a duplicate of this bug. ***
GCC 11.4 is being released, retargeting bugs to GCC 11.5.
*** Bug 110160 has been marked as a duplicate of this bug. ***
The workaround listed in Comment #6 does not work for templated types, unfortunately, making Clang and MSVC more expressive here than GCC. https://godbolt.org/z/obhsqhrbx ``` #include <concepts> #include <sstream> #include <string> #if defined(__GNUC__) && !defined(__clang__) #define COMPILER_IS_GCC 1 #else #define COMPILER_IS_GCC 0 #endif namespace sus::string::__private { template <class A, class B> A& format_to_stream(A&, B); template <class T, class Char> concept StreamCanReceiveString = requires(T& t, std::basic_string<Char> s) { { operator<<(t, s) }; }; /// Consumes the string `s` and streams it to the output stream `os`. template <class Char, StreamCanReceiveString<Char> S> S& format_to_stream(S& os, const std::basic_string<Char>& s) { os << s; return os; } } // namespace sus::string::__private namespace sus::option { template <class T> class Option {}; using namespace ::sus::string::__private; template < class T, #if COMPILER_IS_GCC std::same_as<Option<T> > Sus_ValueType, // Does not deduce T. ***** #endif StreamCanReceiveString<char> Sus_StreamType > inline Sus_StreamType& operator<<( Sus_StreamType& stream, #if COMPILER_IS_GCC const Sus_ValueType& value #else const ::sus::option::Option<T>& value // Does deduce T. **** #endif ) { return format_to_stream(stream, std::string()); } } // namespace sus::option int main() { std::stringstream s; s << sus::option::Option<int>(); } ```
Well for anyone who hits the same issue, it appears that GCC _does_ follow Clang and MSVC in not considering the overload and chasing through the concept resolution if the non-concept types are templates and do not match the caller's arguments. So you need to do: 1) For non-GCC just use: template<fooable T> auto invoke_tag(bar_tag, T it); 2) For GCC non-template type bar_tag use: template<std::same_as<bar_tag> T, fooable U> auto invoke_tag(T, U it); 3) For GCC template type bar_tag, back to 1) template<class P, fooable T> auto invoke_tag(bar_tag<P>, T it); Note also that 2) uses same_as, not convertible_to as in Comment #6, otherwise you can get ambiguous overload resolution if multiple types convert to one, which does not occur in Clang/MSVC with the regular type parameter. This _does_ again result in more code that will compile in Clang/MSVC than in GCC, as it prevents conversions from types that don't have an overload. The macros to do this get rather exciting, if that's of interest to someone in the future: https://github.com/chromium/subspace/pull/253/commits/719500c4d2cbfcfd238d7ee3c5b3d371f40e46c1
*** Bug 110291 has been marked as a duplicate of this bug. ***
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:2154bcd6d43cfd821ca70e1583880c4ed955355d commit r14-3809-g2154bcd6d43cfd821ca70e1583880c4ed955355d Author: Patrick Palka <ppalka@redhat.com> Date: Fri Sep 8 12:02:20 2023 -0400 c++: refine CWG 2369 satisfaction vs non-dep convs [PR99599] As described in detail in the PR, the CWG 2369 resolution has the surprising consequence of introducing constraint recursion in seemingly valid and innocent code. This patch attempts to fix this surpising behavior for the majority of problematic cases. Rather than checking satisfaction before _all_ non-dependent conversions, as specified by the CWG resolution, this patch makes us first check "safe" non-dependent conversions, then satisfaction, then followed by other non-dependent conversions. A conversion is considered "safe" if computing it is guaranteed to not induce template instantiation, and we conservatively determine this by checking for user-declared constructors (resp. conversion functions) in the parm (resp. arg) class type, roughly. PR c++/99599 gcc/cp/ChangeLog: * pt.cc (check_non_deducible_conversions): Add bool parameter passed down to check_non_deducible_conversion. (fn_type_unification): Call check_non_deducible_conversions an extra time before satisfaction with noninst_only_p=true. (conversion_may_instantiate_p): Define. (check_non_deducible_conversion): Add bool parameter controlling whether to compute only conversions that are guaranteed to not induce template instantiation. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-recursive-sat4.C: Make 'Int' non-aggregate in order to preserve intent of the testcase. * g++.dg/cpp2a/concepts-nondep4.C: New test.
The unwanted constraint recursion should be fixed in GCC 14 for the most common cases.
*** Bug 108393 has been marked as a duplicate of this bug. ***
Here's a very small reproducer: template<typename T> struct indirect { template<typename U> requires // (!is_indirect<U>) && requires (const T& t, const U& u) { t == u; } friend constexpr bool operator==(const indirect&, const U&) { return false; } T* _M_ptr{}; }; indirect<int> i; bool b = i == 1; It compiles with trunk, although my real case that this was reduced from doesn't. It works if you uncomment the extra constraint and define: template<typename T> class indirect; template<typename T> constexpr bool is_indirect = false; template<typename T> constexpr bool is_indirect<indirect<T>> = true;
Here we go, this still fails on trunk, just by making the data member private: template<typename T> class indirect { public: template<typename U> requires // (!is_indirect<U>) && requires (const T& t, const U& u) { t == u; } friend constexpr bool operator==(const indirect&, const U&) { return false; } private: T* _M_ptr{}; }; indirect<int> i; bool b = i == 1;
(In reply to Jonathan Wakely from comment #22) > Here we go, this still fails on trunk, just by making the data member > private: That's because for a non-dependent conversion to a class type we only check it before constraints if it's an aggregate class (otherwise it might have a constructor template, which means the conversion might instantiate things making it unsafe to check before constraints). Maybe we should consider refining the heuristic further. I believe the code is strictly speaking invalid though.
I have the workaround now, so not urgent for me
It looks like PR 104606 is another case. #include <optional> #include <variant> #include <vector> struct Value : public std::variant<std::vector<Value>> { }; struct Comparator { template <typename T> bool operator<=(const T &rhs) { return true; } }; int main() { auto test2 = Comparator() <= std::optional<Value>{}; } Preprocessing this with 10.4 and compiling with 11.1 fails, bisection shows it started to fail with r11-2774.
What's bizarre about the PR 104606 case is that this fixes it: --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -1431,7 +1431,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __cpp_lib_three_way_comparison template<typename _Tp, typename _Up> requires (!__is_optional_v<_Up>) - && three_way_comparable_with<_Tp, _Up> + && three_way_comparable_with<_Up, _Tp> constexpr compare_three_way_result_t<_Tp, _Up> operator<=>(const optional<_Tp>& __x, const _Up& __v) { return bool(__x) ? *__x <=> __v : strong_ordering::less; }
GCC 11 branch is being closed.