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.

Reply via email to