llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.34k stars 11.7k forks source link

Diagnostics for P2266 (C++23) are poor #88089

Open tiagomacarios opened 6 months ago

tiagomacarios commented 6 months ago

The diagnostics for the following code are very poor: https://godbolt.org/z/5nv9dcr14

struct M {
    M();
    M(M&);
};

struct S {
    int m1;
    M m2;
};

S fun() {
    S s{};
    return s;
}

clang's output:

<source>:15:12: error: no viable conversion from 'S' to 'int'
   15 |     return s;
      |            ^

gcc's output:

<source>: In function 'S fun()':
<source>:15:12: error: use of deleted function 'S::S(S&&)'
   15 |     return s;
      |            ^
<source>:8:8: note: 'S::S(S&&)' is implicitly deleted because the default definition would be ill-formed:
    8 | struct S {
      |        ^
<source>: At global scope:
<source>:8:8: error: cannot bind non-const lvalue reference of type 'M&' to an rvalue of type 'M'
<source>:5:7: note:   initializing argument 1 of 'M::M(M&)'
    5 |     M(M&);
      |       ^~
<source>: In function 'S fun()':
<source>:15:12: note: use '-fdiagnostics-all-candidates' to display considered candidates
   15 |     return s;
      |            ^
frederick-vs-ja commented 6 months ago

The move ctor of S is defaulted and implicitly deleted. Per [over.match.funcs.general]/9, it is ignored in overload resolution. However, it seems better to figure it out in diagnostic message.

Currently, Clang is seemingly considering invalid conversion sequence.

Example 1

struct M {
    M();
    M(M&);
};

struct S;
struct T {
    T() = default;
    T(const S&); // make lvalue of S convertible to T
};

struct S {
    T m1;
    M m2;
};

S fun() {
    S s{};
    return s; // Clang considers this well-formed in C++23 given S& is convertible to T. NRVO enabled.
}

Example 2

struct M {
    M();
    M(M&);
};

struct S;
struct T {
    T() = default;
    T(const S&);
};

struct S {
    T m1;
    M m2;
};

S fun() {
    S s{};
    return static_cast<S&&>(s); // Clang considers this well-formed since C++20!
}

Perhaps Clang overly generalizes the change in P0960R3 to the copy-initialization in return statements. Per CWG2824, aggregate initialization shouldn't be considered in these contexts.

llvmbot commented 6 months ago

@llvm/issue-subscribers-clang-frontend

Author: Tiago (tiagomacarios)

The diagnostics for the following code are very poor: https://godbolt.org/z/5nv9dcr14 ```c++ struct M { M(); M(M&); }; struct S { int m1; M m2; }; S fun() { S s{}; return s; } ``` clang's output: ``` <source>:15:12: error: no viable conversion from 'S' to 'int' 15 | return s; | ^ ``` gcc's output: ``` <source>: In function 'S fun()': <source>:15:12: error: use of deleted function 'S::S(S&&)' 15 | return s; | ^ <source>:8:8: note: 'S::S(S&&)' is implicitly deleted because the default definition would be ill-formed: 8 | struct S { | ^ <source>: At global scope: <source>:8:8: error: cannot bind non-const lvalue reference of type 'M&' to an rvalue of type 'M' <source>:5:7: note: initializing argument 1 of 'M::M(M&)' 5 | M(M&); | ^~ <source>: In function 'S fun()': <source>:15:12: note: use '-fdiagnostics-all-candidates' to display considered candidates 15 | return s; | ^ ```