https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124694
Bug ID: 124694
Summary: reflection: data_member_spec &
__builtin_constexpr_diag ignore string contents after
null character
Product: gcc
Version: 16.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: ivan.lazaric.gcc at gmail dot com
Target Milestone: ---
```cpp
#define private public // just so i can access data_member_options internals
#include <meta>
constexpr std::string escape(std::string_view sv) {
std::string out;
for (auto c : sv) {
if (c == '\0') out += "\\0";
else if (c == '\\') out += "\\\\";
else out += c;
}
return out;
}
struct S;
consteval {
std::meta::data_member_options opts;
char name[] = "hello\0world";
std::string_view name_sv(name, sizeof(name) - 1);
opts.name = name_sv;
auto mem = data_member_spec(^^int, opts);
define_aggregate(^^S, {mem});
__builtin_constexpr_diag(0, "name_sv", name_sv);
__builtin_constexpr_diag(0, "name_sv_escaped", escape(name_sv));
__builtin_constexpr_diag(0, "opt_name", opts.name->_M_s);
__builtin_constexpr_diag(0, "opt_name_escaped", escape(opts.name->_M_s));
__builtin_constexpr_diag(0, "member_name",
identifier_of(nonstatic_data_members_of(^^S,
std::meta::access_context::unchecked())[0]));
__builtin_constexpr_diag(0, "member_name_escaped",
escape(identifier_of(nonstatic_data_members_of(^^S,
std::meta::access_context::unchecked())[0])));
}
```
Flags "-std=c++26 -freflection"
Compiler output, modulo diagnostic context:
```
bar.cpp:23:29: note: constexpr message: hello [name_sv]
bar.cpp:24:29: note: constexpr message: hello\0world [name_sv_escaped]
bar.cpp:25:29: note: constexpr message: hello [opt_name]
bar.cpp:26:29: note: constexpr message: hello\0world [opt_name_escaped]
bar.cpp:27:29: note: constexpr message: hello [member_name]
bar.cpp:28:29: note: constexpr message: hello [member_name_escaped]
```
Both data_member_spec() and __builtin_constexpr_diag() silently ignore string
contents after the first null character
For data_member_spec(), my reading of the spec is that this should throw,
because "hello\0world" is not a valid identifier
* (exposition-only) contents should be "hello\0world" due to
https://eel.is/c++draft/meta#reflection.define.aggregate-3
* contents is not a valid identifier, so throw
https://eel.is/c++draft/meta#reflection.define.aggregate-5.2.2
For __builtin_constexpr_diag(), it's not part of the C++ standard,
but I don't know if current behaviour is the most user friendly.
I am also unsure what behaviour would be best.
Some alternatives:
* escaping (a la `std::format("{:?}", str)`)
* prints as currently, but warns if a null character was seen in contents
* prints full contents without escaping (so null character is printed as well)
* throw/error if arg contains null character
I think I am leaning towards option 3 (print everything as-is), but unsure.