Following program generates an error for a struct packed with __attribute__((packed)) but not with #pragma pack(1) Compiled as g++ -O2 -std=c++17 -Wall -Wextra -Wpedantic -Wstrict-aliasing -fstrict-aliasing Error: the value of 's2' is not usable in a constant expression 27 | constexpr auto sz2 = std::size(s2.i); Web compiler: https://godbolt.org/z/h-7i4p #include <iterator> #pragma pack(1) struct S1 { int i[10]; } ; #pragma pack() struct S2 { int i[10]; } __attribute__((packed)) // comment this to make it compile ; #define LEN(a) (sizeof(a) / sizeof(a[0])); void f() { S1 s1{}; constexpr auto sz1 = std::size(s1.i); (void)sz1; S2 s2{}; constexpr auto sz2 = std::size(s2.i); (void)sz2; constexpr auto sz3 = LEN(s2.i); (void)sz3; }
possibly related to bug 60972
Testcase without includes: typedef __SIZE_TYPE__ size_t; template <typename T, size_t N> constexpr size_t size (const T (&)[N]) noexcept { return N; } #pragma pack(1) struct S1 { int i[10]; }; #pragma pack() struct __attribute__((packed)) S2 { int i[10]; }; #define LEN(a) (sizeof(a) / sizeof(a[0])); size_t foo () { S1 s1{}; constexpr auto sz1 = size (s1.i); S2 s2{}; constexpr auto sz2 = size (s2.i); constexpr auto sz3 = LEN(s2.i); return sz1 + sz2 + sz3; }
The difference between those two is that s1.i FIELD_DECL doesn't have DECL_PACKED set on it (a bug in the #pragma pack support?), while with packed attribute it does. DECL_PACKED on the FIELD_DECL in s2 is set during check_field_decls called from check_bases_and_members from finish_struct*. For S1, TYPE_PACKED is actually never set, and thus DECL_PACKED neither. The reason the s2 case is rejected is that lvalue_kind returns something with clk_packed on it, forcing a temporary. That was added in https://gcc.gnu.org/ml/gcc-patches/2003-07/msg01664.html CCing the author. Note, even in the C FE pragma pack actually doesn't cause TYPE_PACKED/DECL_PACKED to be set, it isn't really identical to what packed attribute does either, e.g. I believe we should only set DECL_PACKED on FIELD_DECLs where packed attribute on the containing type or pragma pack or -fpack-struct= etc. actually lowered the alignment over what it would normally have.
Since accessing a misaligned variable is undefined behaviour, I don't think it should be permitted in constant expressions (at least for the cases where the alignment is reduced from its natural value).