[Clang] Trivial copy constructor of classes with a `nullptr_t` member tries to read it during constant expressions
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
An lvalue-to-rvalue conversion on a `nullptr_t` glvalue doesn't actually read it and should always produce a null pointer constant ([[conv.lval]p(3.1)](https://wg21.link/conv.lval#3.1)).
The implicitly defined copy constructor for a class should perform a memberwise copy. So there should be similar behaviour regardless of the `EXPLICIT_CONSTRUCTORS` macro in the following: ()
```c++
struct X {
#ifdef EXPLICIT_CONSTRUCTORS
constexpr X() noexcept {}
constexpr X(const X& other) noexcept : n(other.n) {}
#endif
decltype(nullptr) n;
};
int main() {
X x;
void* p = &x;
void* q = &x;
__builtin_memcpy(&x.n, &p, sizeof p);
X y = x;
__builtin_memcpy(&p, &y.n, sizeof p);
return __builtin_memcmp(&p, &q, sizeof p) == 0 ? 1 : 0;
}
```
When `EXPLICIT_CONSTRUCTORS` is defined, the program returns 1 (as expected, `n(other.n)` initialises it with a null pointer constant without reading), but when it is not defined, the program returns 0 (The 'trivial' copy constructor does a bytewise copy).
This is not entirely a bug, it's technically just a missed optimisation because the object representation of `nullptr_t` objects is not specified (so can be treated as all padding, also discussed in #167613). It might become an issue if the `nullptr` is volatile (and so shouldn't be read). It is an issue in a constant expression (where it is explicitly allowed to perform lvalue-to-rvalue on any `std::nullptr_t` glvalue, [[expr.const]p(9.9.1)](https://wg21.link/expr.const#9.9.1)): ()
```c++
struct X {
#ifdef EXPLICIT_CONSTRUCTORS
constexpr X() noexcept {}
constexpr X(const X& other) noexcept : n(other.n) {}
#endif
decltype(nullptr) n;
};
extern X x;
constexpr X y = x; // Only compiles with EXPLICIT_CONSTRUCTORS defined
```
Contributor guide
Research direction
Reproduce the two reduced examples from the issue, including the Godbolt cases, with and without EXPLICIT_CONSTRUCTORS. Start by tracing Clang's handling of trivial copy construction and constant expressions for decltype(nullptr) members. Done means the implicitly defined copy constructor has the same relevant behavior as the explicit memberwise constructor, including acceptance of the constexpr case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100