llvm / llvm-project

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

Clang allows, in constexpr, trivial union copy constructor to read uninitialized subobjects of active member #59329

Open hubert-reinterpretcast opened 1 year ago

hubert-reinterpretcast commented 1 year ago

Lvalue-to-rvalue conversion of objects having indeterminate value is not allowed during evaluation of a core constant expression (see N4919 subclause 7.7 [expr.const] bullet 5.11). Clang allows such lvalue-to-rvalue conversion during the constant evaluation of trivial union copy constructors.

In the case below, the y member of u has indeterminate value (as does the corresponding bytes of the object representation) when the initialization of v is performed. ICC correctly diagnoses the issue.

Compiler Explorer link: https://godbolt.org/z/6174nqhbE

SOURCE (<stdin>):

struct A {
  int x, y;
  constexpr A() {}
};

union U { A x; };

constexpr int f() {
  U u = { A() };
  u.x.x = 42;
  U v = u;
  return v.x.x;
}

extern constexpr int x = f();

COMPILER INVOCATION:

clang++ -xc++ -fsyntax-only -std=c++2a -

ACTUAL OUTPUT:

(Clean compile)

EXPECTED OUTPUT:

(Error that x is not initialized by a constant expression)

COMPILER VERSION INFO (clang++ -v):

clang version 16.0.0 (https://github.com/llvm/llvm-project.git 629a29cacc09def1821def8f4d5455b4be5ac9b1)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/wandbox/clang-head/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/10
Candidate multilib: .;@m64
Selected multilib: .;@m64
llvmbot commented 1 year ago

@llvm/issue-subscribers-c-20

zygoloid commented 1 year ago

I don't think any lvalue-to-rvalue conversion happens in a defaulted copy constructor for a union. Per [class.copy.ctor]/15:

The implicitly-defined copy/move constructor for a union X copies the object representation ([basic.types.general]) of X. For each object nested within ([intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is identified (if the object is a subobject) or created (otherwise), and the lifetime of o begins before the copy is performed.

I wouldn't interpret "copies the object representation" as performing an lvalue-to-rvalue conversion. If you do, then you also can't copy a union that's fully initialized but has padding, because the object representation can contain indeterminate values in that case too.

hubert-reinterpretcast commented 1 year ago

I wouldn't interpret "copies the object representation" as performing an lvalue-to-rvalue conversion. If you do, then you also can't copy a union that's fully initialized but has padding, because the object representation can contain indeterminate values in that case too.

That's CWG 2658. The wording was new from a paper that is now the subject of multiple Core Issues.