llvm / llvm-project

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

[Clang] constexpr checking not considering copy elision #116778

Open aywala opened 1 week ago

aywala commented 1 week ago
#include <iostream>

struct Elem {
    unsigned short data;
    constexpr Elem(unsigned int a) noexcept : data(static_cast<unsigned short>(a)){};
    Elem(const Elem& elem) : data(elem.data) { std::cout<<"call copy constructor\n"; }; // error with clang -std=c++14, ok with gcc and msvc
    // constexpr Elem(const Elem& elem) : data(elem.data) {}; // ok
};

 constexpr Elem foo() noexcept {
    return Elem{0x0400};
}

int main() {
    Elem e1 = foo();
    std::cout<<e1.data<<std::endl;
}
<source>:10:17: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
   10 |  constexpr Elem foo() noexcept {
      |                 ^~~
<source>:11:12: note: non-constexpr constructor 'Elem' cannot be used in a constant expression
   11 |     return Elem{0x0400};
      |            ^
<source>:6:5: note: declared here
    6 |     Elem(const Elem& elem) : data(elem.data) { std::cout<<"call copy constructor\n"; }; // error with clang -std=c++14, ok with gcc and msvc
      |     ^
1 error generated.

With -std=c++14 flag, Clang performs the copy elision for the return statement in foo(), but still reports error about the copy constructor. I know copy elision is not guaranteed with pre-C++17, but since clang does perform it, there should not be an error. When C++ standard is not specified, Clang-15 reports an error while higher versions do not. GCC and MSVC have no problem with this code sample.

https://godbolt.org/z/af5aGzn1a

llvmbot commented 1 week ago

@llvm/issue-subscribers-c-14

Author: Aywala (aywala)

```cpp #include <iostream> struct Elem { unsigned short data; constexpr Elem(unsigned int a) noexcept : data(static_cast<unsigned short>(a)){}; Elem(const Elem& elem) : data(elem.data) { std::cout<<"call copy constructor\n"; }; // error with clang -std=c++14, ok with gcc and msvc // constexpr Elem(const Elem& elem) : data(elem.data) {}; // ok }; constexpr Elem foo() noexcept { return Elem{0x0400}; } int main() { Elem e1 = foo(); std::cout<<e1.data<<std::endl; } ``` With -std=c++14 flag, Clang performs the copy elision for the return statement in foo(), but still reports error about the copy constructor. I know copy elision is not guaranteed with pre-C++17, but since clang does perform it, there should not be a error.
llvmbot commented 1 week ago

@llvm/issue-subscribers-clang-frontend

Author: Aywala (aywala)

```cpp #include <iostream> struct Elem { unsigned short data; constexpr Elem(unsigned int a) noexcept : data(static_cast<unsigned short>(a)){}; Elem(const Elem& elem) : data(elem.data) { std::cout<<"call copy constructor\n"; }; // error with clang -std=c++14, ok with gcc and msvc // constexpr Elem(const Elem& elem) : data(elem.data) {}; // ok }; constexpr Elem foo() noexcept { return Elem{0x0400}; } int main() { Elem e1 = foo(); std::cout<<e1.data<<std::endl; } ``` With -std=c++14 flag, Clang performs the copy elision for the return statement in foo(), but still reports error about the copy constructor. I know copy elision is not guaranteed with pre-C++17, but since clang does perform it, there should not be a error.
frederick-vs-ja commented 1 week ago

See CWG2022 and CWG2278.

I'm not sure whether RVO should be mandatory or disallowed in constant evaluation in C++11/14. At the time when CWG2022 got accepted, RVO was considered as copy elision. However, since C++17 (thanks to P0135R1), RVO is no longer considered as copy elision.

CC @zygoloid @jicama.