Description
Accessing members of a non-type template parameter of struct type sometimes results in incorrect values or internal compiler errors if the struct has padding.
In particular, reading members after the padding returns the bits of other members. Accessing the last member seems to raise an out-of-bounds error in the compiler.
Reproducible on Clang 18, 19, 20 and trunk (3cc78a8), both on Compiler Explorer and locally (using the releases from https://apt.llvm.org).
https://godbolt.org/z/Y8rf46Gvv
// clang++ -std=c++20
#include <fmt/format.h>
struct Config {
unsigned char a = 0xAA;
unsigned int b = 0xBBBBBBBB;
unsigned char c = 0xCC;
unsigned char d = 0xDD;
unsigned char e = 0xEE;
};
template <Config Conf>
void foo() {
fmt::print("Conf.a = {:02X} ", Conf.a);
fmt::print("Conf.b = {:08X} ", Conf.b);
fmt::print("Conf.c = {:02X} ", Conf.c);
fmt::print("Conf.d = {:02X} ", Conf.d);
#ifndef __clang__ // fatal error: error in backend: Invalid size request on a scalable vector.
fmt::print("Conf.e = {:02X} ", Conf.e);
#endif
fmt::println("");
auto &conf = Conf;
fmt::print("conf.a = {:02X} ", conf.a);
fmt::print("conf.b = {:08X} ", conf.b);
fmt::print("conf.c = {:02X} ", conf.c);
fmt::print("conf.d = {:02X} ", conf.d);
fmt::print("conf.e = {:02X} ", conf.e);
fmt::println("");
}
int main() { foo<{}>(); }
Conf.a = AA Conf.b = 00EEDDCC Conf.c = DD Conf.d = EE
conf.a = AA conf.b = BBBBBBBB conf.c = CC conf.d = DD conf.e = EE
Note how Conf.b
contains the bits of Conf.{c,d,e}
, and the values of Conf.{c,d}
are also offset by one byte. Trying to print Conf.e
crashes the compiler.
The behavior seems to be sensitive to minor changes to the code. For example, storing Conf
in a constexpr variable before printing does not trigger the bug. In the code base where I originally encountered the issue, passing Conf.b
as a template argument to another function template <int I> void bar();
would actually call bar<0x00EEDDCC>()
, but I'm having trouble reproducing this in a standalone program.