Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[class.compare.default] p1 The condition of implicitly defining a defaulted comparison operator function CWG2546 #5336

Closed
xmh0511 opened this issue Mar 7, 2022 · 8 comments · Fixed by #6906
Labels
cwg Issue must be reviewed by CWG.

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented Mar 7, 2022

[class.compare.default] p1 just says

A comparison operator function for class C that is defaulted on its first declaration and is not defined as deleted is implicitly defined when it is odr-used or needed for constant evaluation.

Consider this example

struct HasNoLessThan { };
struct C{
    friend HasNoLessThan operator <=>(C const&, C const&);
    bool operator <(C const&) const /*noexcept*/ = default;
};
int main(){
   using ptr = decltype(&C::operator<); // #1
}

Since &C::operator< appears as an unevaluated operand, function C::operator < is not odr-used by this expression. However, Clang reports a diagnosis that:

in evaluation of exception specification for 'C::operator<' needed here

[except.spec] p11 says

The exception specification for a comparison operator function ([over.binary]) without a noexcept-specifier that is defaulted on its first declaration is potentially-throwing if and only if any expression in the implicit definition is potentially-throwing.

So, It is reasonable to check the definition of the defaulted function. However, we just define two contexts that can cause the implicit definition for the defaulted function. Checking the expressions in the implicit definition is not defined to be odr-used nor constant evaluation of the function. It seems that we lack the case that determines the exception specification of a defaulted comparison operator function.

Improvement:

A comparison operator function for class C that is defaulted on its first declaration and is not defined as deleted is implicitly defined when it is odr-used, needed for constant evaluation, or needed to determine exception specification(if necessary).

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented Mar 7, 2022

[dcl.fct.def.delete]/2

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

So even if the implicit exception specification is not needed, decltype(&C::operator<) is ill-formed.

IIUC this is a bug of clang. Please report it here.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 7, 2022

[dcl.fct.def.delete]/2

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

So even if the implicit exception specification is not needed, decltype(&C::operator<) is ill-formed.

IIUC this is a bug of clang. Please report it here.

No, the defaulted function is not deleted. HasNoLessThan operator <=>(C const&, C const&) is a usable candidate and a rewritten candidate for x < y, as per [class.compare.secondary] p2 and [over.match.oper#3.4]. Instead, I think it is a bug of GCC, which says that defaulted operator function is deleted.

@frederick-vs-ja
Copy link
Contributor

Are you explaining why the comment in [class.compare.secondary] p3 is wrong? I believe it's right, extremely.

When the rewritten candidate is found, a nested overload resolution is performed for the written expression and then fails. According to [over.match.general] p4, the rewritten candidate is not usable.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 7, 2022

Are you explaining why the comment in [class.compare.secondary] p3 is wrong? I believe it's right, extremely.

When the rewritten candidate is found, a nested overload resolution is performed for the written expression and then fails. According to [over.match.general] p4, the rewritten candidate is not usable.

"the rewritten candidate is not usable" No, it is usable. First, HasNoLessThan operator <=>(C const&, C const&) is a candidate for x < y, and all operands of the expression can be as the arguments in the call of HasNoLessThan operator <=>(C const&, C const&) since the operands are of type const C. According to [over.match.general] p2 and [over.match.general] p3, HasNoLessThan operator <=>(C const&, C const&) is the unique best viable function. Hence, the overload resolution succeeds.

a nested overload resolution is performed for the written expression and then fails.

IMO, overload resolution only considers the candidate itself. It shouldn't consider subsequent interpretation for the expression. The clue is here:

If a rewritten operator<=> candidate is selected by overload resolution for an operator @, x @ y is interpreted as...

That is, only after the overload resolution for the expression x@y has succeeded and selected rewritten operator<=> candidate will further interpretation be done. [class.compare.secondary] p2 just requires that

overload resolution ([over.match]), as applied to x @ y, does not result in a usable candidate

It didn't say the reinterpretation of x < y, in this case, which is (x <=> y) < 0, shall have a usable candidate or be well-formed.


[over.match.general] p1 states that

Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are to be the arguments of the call and a set of candidate functions that can be called based on the context of the call.

For expression x < y, the set of candidate functions is { HasNoLessThan operator <=>(C const&, C const&) } as per [over.match.oper] p3 and the list of arguments are x, y as per [over.match.oper] p7. Anyway, the overload resolution can succeed.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 7, 2022

If the comment is the intent of [class.compare.secondary] p2, it may be improved to

The operator function with parameters x and y is defined as deleted if

  • the candidate selected by overload resolution, as applied to x @ y, is not a rewritten candidate, or
  • For expression x @ y, it is ill-formed.

the candidate selected by overload resolution for x < y is HasNoLessThan operator <=>(C const&, C const&), which is a rewritten candidate, the first bullet is not violated. Since the selected candidate is a rewritten operator<=> candidate, the expression x < y is interpreted as (x <=> y) < 0, which is ill-formed; the first operand of < has type HasNoLessThan while the second has the type int, they are not comparable.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 7, 2022

a nested overload resolution is performed for the written expression and then fails.

Another example that can prove we shouldn't augment the extent of considering overload resolution for this definition is:

struct HasNoLessThan { 
    operator int(){
        return 0;
    }
};
struct C{
    friend HasNoLessThan operator <=>(C const&, C const&);
    bool operator <(C const&) const = default;
};
int main(){
   using ptr = decltype(&C::operator<);
}

Both implementations accept this example. The nested overload resolution will be performed for the operator < within (x<=>y) < 0. The selected candidate is the built-in one, which would violate the second bullet in [class.compare.secondary] p2. It is not reasonable to augment the extent of the overload resolution.

@jensmaurer
Copy link
Member

jensmaurer commented Mar 7, 2022

The "selected candidate" in [class.compare.secondary] p2 is operator <=>, not the second-order overload resolution for operator<.

The fixes in CWG2546 should address this, too.

@jensmaurer jensmaurer added the cwg Issue must be reviewed by CWG. label Mar 7, 2022
@jensmaurer jensmaurer changed the title [class.compare.default] p1 The condition of implicitly defining a defaulted comparison operator function [class.compare.default] p1 The condition of implicitly defining a defaulted comparison operator function CWG2546 Mar 7, 2022
@frederick-vs-ja
Copy link
Contributor

Should have been fixed by #6906 (eef4c2b).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cwg Issue must be reviewed by CWG.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants