llvm / llvm/llvm-project

[Clang] Trivial copy constructor of classes with a `nullptr_t` member tries to read it during constant expressions

Open
#177,791 4 comments 0 reactions 0 assignees View on GitHub
clang:frontend constexpr rejects-valid
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.