https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127046

            Bug ID: 127046
           Summary: Trailing array element loses its initializer when the
                    constexpr function it's declared in is const-evaluated
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: vincenzo.cal.main at gmail dot com
  Target Milestone: ---

A regression in r16-3022-gbc42128330c causes a local array of objects with non
trivial destructor to silently lose the initializer of its last element
when the constexpr function it's declared in is const-evaluated anywhere
in the same translation unit.

This bug was initially detected as a failure in the libc++ test suite, in
is the associated issue:

https://github.com/llvm/llvm-project/issues/215497

## Testcase

repro.cpp:
``` cpp
struct T { char padding[4] = {'x'}; constexpr ~T() {} };
constexpr bool test() {
  T array[3] = {};
  return array[0].padding[0]=='x' && array[1].padding[0]=='x' &&
array[2].padding[0]=='x';
}
int main() { return test() ? 0 : 1; }
```

repro.ii:
```
# 0 "repro.cpp"
# 0 "<built-in>"
# 0 "<command-line>"
# 1
"/nix/store/29isvm6vfq10c528i31igdiy3fnvjcm3-glibc-2.42-67-dev/include/stdc-predef.h"
1 3
# 0 "<command-line>" 2
# 1 "repro.cpp"
struct T { char padding[4] = {'x'}; constexpr ~T() {} };
constexpr bool test() {
  T array[3] = {};
  return array[0].padding[0]=='x' && array[1].padding[0]=='x' &&
array[2].padding[0]=='x';
}
int main() { return test() ? 0 : 1; }
```

To reproduce:

``` bash
nix shell github:NixOS/nixpkgs/56c02bc00adcf003215cc4bd996d6efaf4cff188#gcc15 \
-c sh -c 'g++ -std=c++23 -O0 repro.cpp -o r15 && ./r15'; echo "15: $?"

nix shell github:NixOS/nixpkgs/56c02bc00adcf003215cc4bd996d6efaf4cff188#gcc16 \
-c sh -c 'g++ -std=c++23 -O0 repro.cpp -o r16 && ./r16'; echo "16: $?"
```
The bug needs no options beyond `-std`. `-O0` is necessary only for this test
case as with `-O1` the test function is inlined in main.
Please check `repro-volatile-fnptr.cpp` in appendix for a test case that
does not require the `-O0` flag .

Godbolt assembly comparison:
https://godbolt.org/z/YPKs3cd17

## Versions

Tested on: x86_64-unknown-linux-gnu (NixOS 26.11, Linux 7.1.3, glibc 2.42)
Known versions to work: 15.1.0, 15.2.0, 15.3.0
Known versions to fail: 16.1.0, 16.2.0

Independently to: -std=[c++20, c++23, c++26]

Bisected by diffing the generated GIMPLE to:

```
bc42128330c0ea70a015b74b655cb8c48b6a8c06
"c++: clobber object on placement new [PR121068]"
Jason Merrill, 2025-08-05
```

## GCC configuration flags

### GCC 16.1.0 (fails)

```
Using built-in specs.
COLLECT_LTO_WRAPPER=/nix/store/r9minpvfk7dgyn3vf5a0lvnmvh2y286k-gcc-16.1.0/libexec/gcc/x86_64-unknown-linux-gnu/16.1.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: ../gcc-16.1.0/configure
--prefix=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gcc-16.1.0
--with-gmp-include=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gmp-with-cxx-6.3.0-dev/include
--with-gmp-lib=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gmp-with-cxx-6.3.0/lib
--with-mpfr-include=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-mpfr-4.2.2-dev/include
--with-mpfr-lib=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-mpfr-4.2.2/lib
--with-mpc=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-libmpc-1.4.1
--with-native-system-header-dir=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-glibc-2.42-67-dev/include
--with-build-sysroot=/
--with-gxx-include-dir=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gcc-16.1.0/include/c++/16.1.0/
--program-prefix= --enable-lto --disable-libstdcxx-pch
--without-included-gettext --with-system-zlib --enable-static
--enable-languages=c,c++ --disable-multilib --enable-plugin
--with-isl=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-isl-0.20
--disable-bootstrap --enable-default-pie --build=x86_64-unknown-linux-gnu
--host=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 16.1.0 (GCC) 
```

### GCC 15.3.0 (works)

```
Using built-in specs.
COLLECT_LTO_WRAPPER=/nix/store/7sh9061yj9yl20ndc00rqw4d0bd80m1j-gcc-15.3.0/libexec/gcc/x86_64-unknown-linux-gnu/15.3.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: ../gcc-15.3.0/configure
--prefix=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gcc-15.3.0
--with-gmp-include=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gmp-6.3.0-dev/include
--with-gmp-lib=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gmp-6.3.0/lib
--with-mpfr-include=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-mpfr-4.2.2-dev/include
--with-mpfr-lib=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-mpfr-4.2.2/lib
--with-mpc=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-libmpc-1.4.1
--with-native-system-header-dir=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-glibc-2.42-67-dev/include
--with-build-sysroot=/
--with-gxx-include-dir=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gcc-15.3.0/include/c++/15.3.0/
--program-prefix= --enable-lto --disable-libstdcxx-pch
--without-included-gettext --with-system-zlib --enable-static
--enable-languages=c,c++ --disable-multilib --enable-plugin --disable-libcc1
--with-isl=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-isl-0.20
--disable-bootstrap --enable-default-pie --build=x86_64-unknown-linux-gnu
--host=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 15.3.0 (GCC) 
```

## Test case without opt flags
repro-volatile-fnptr.cpp
``` cpp
struct T { char padding[4] = {'x'}; constexpr ~T() {} };
constexpr bool test() {
  T array[3] = {};
  return array[0].padding[0]=='x' && array[1].padding[0]=='x' &&
array[2].padding[0]=='x';
}
static_assert(test());
bool (* volatile fp)() = test;
int main() { return fp() ? 0 : 1; }
```
repro-volatile-fnptr.ii
```
# 0 "repro-volatile-fnptr.cpp"
# 0 "<built-in>"
# 0 "<command-line>"
# 1
"/nix/store/29isvm6vfq10c528i31igdiy3fnvjcm3-glibc-2.42-67-dev/include/stdc-predef.h"
1 3
# 0 "<command-line>" 2
# 1 "repro-volatile-fnptr.cpp"
struct T { char padding[4] = {'x'}; constexpr ~T() {} };
constexpr bool test() {
  T array[3] = {};
  return array[0].padding[0]=='x' && array[1].padding[0]=='x' &&
array[2].padding[0]=='x';
}
static_assert(test());
bool (* volatile fp)() = test;
int main() { return fp() ? 0 : 1; }
```

I would be happy to pick up the bug and develop a patch, if you agree.

Reply via email to