On Wed, 23 Jul 2025, Jason Merrill wrote: > On 7/23/25 3:46 PM, Patrick Palka wrote: > > As a follow-up to r16-2448-g7590c14b53a762, this patch attempts to teach > > build_min_non_dep_op_overload how to rebuild all rewritten comparison > > operators, not just != -> == ones, so that we don't incorrectly repeat > > the unqualified name lookup at instantiation time. > > Talking about mangling earlier made me wonder how we were handling > non-dependent operator expressions, and indeed it seems we get it wrong since > GCC 6: > > struct A { }; > A operator+(A,A); > template <class T> > void f(decltype(T(),A()+A())) { } > int main() > { > f<int>(A()); // oops, mangles as operator+(A(),A()) instead of A()+A() > } > > while clang and EDG corretly use the latter mangling. > > With the current code I would think we could fix this by handling > CALL_EXPR_OPERATOR_SYNTAX in mangle.cc, but your patch (and indeed the earlier > one) would further obscure the original syntax.
Does this mean it's also incorrect to mangle the ordinary non-dependent f(0) call in: template<class T> void f(T); template<class T> decltype(T(),f(0)) g(); int main() { g<int>(); } as f<int>(0) i.e. with an explicit template argument list even though it was written without one? Clang mangles it as f<int>(0) too, not sure about EDG. This changed in GCC 12 with the non-dependent overload set pruning optimization. And does this have any declaration matching implications? Say for struct A { }; template<class T> int operator+(A,T); template<class T> decltype(T(),A()+A()) f(); A operator+(A,A); template<class T> decltype(T(),A()+A()) f(); int main() { f<int>(); } should we still reject the f<int>() call as ambiguous, or treat the second declaration as a redeclaration (since they have the same mangling?) This seems related to CWG1321 but for non-dependent calls. > > > While working on this I noticed we'll seemingly never create a rewritten > > operator expression that is in terms of a built-in operator, since we > > could have used a built-in operator directly in the first place, which > > simplifies things. I think this also means the extract_call_expr > > handling of rewritten operators is wrong since it inspects for LT_EXPR, > > SPACESHIP_EXPR etc directly, so this patch just removes it in passing. > > That code is not about rewriting in terms of a built-in operator, it was to > look through the operations added by the rewriting, e.g. TRUTH_NOT_EXPR for > operator!= to !(operator==) to find the actual call to the operator > underneath. The TRUTH_NOT_EXPR case seems fine, but AFAICT the LT_EXPR, GT_EXPR etc cases are dead code because we'll never have an LT/GT/etc_EXPR of an operator<=> call, since operator<=> must return std::strong/weak/partial_ordering which are class types, and so 0 < (x <=> y) must always resolve to a user-defined operator< etc. Oh wait, that'll only be true after the rest of the patch is applied... otherwise non-dependent templated rewritten operator expressions will indeed contain LT/GT/etc_EXPR. > > It does look like that's unnecessary now because build_new_op calls > extract_call_expr before adding those decorations, so I don't object to > removing it, but please make that a separate patch. Sounds good. > > Jason > >