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

Reply via email to