https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123613
Bug ID: 123613
Summary: [reflection] internal compiler error: tree check:
expected record_type or union_type or qual_union_type,
have template_type_parm in lookup_base, at
cp/search.cc:265
Product: gcc
Version: 16.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: mpolacek at gcc dot gnu.org
Target Milestone: ---
```
#include <meta>
#include <charconv>
#include <string>
#include <algorithm>
template <std::meta::info ...Ms>
struct Outer {
struct Inner;
consteval {
define_aggregate(^^Inner, {Ms...});
}
};
template <std::meta::info ...Ms>
using Cls = Outer<Ms...>::Inner;
template <typename T, auto ... Vs>
constexpr auto construct_from = T{Vs...};
consteval std::meta::info parse_json(std::string_view json) {
auto cursor = json.begin();
auto end = json.end();
auto is_whitespace = [](char c) {
return c == ' ' || c == '\n' || c == '\t';
};
auto skip_whitespace = [&] -> void {
while (is_whitespace(*cursor)) cursor++;
};
auto expect_consume = [&] (char c) -> void {
skip_whitespace();
if (cursor == end || *(cursor++) != c) throw "unexpected character";
};
auto parse_until = [&](std::vector<char> delims, std::string &out) -> void {
skip_whitespace();
while (cursor != end &&
!std::ranges::any_of(delims, [&](char c) { return c == *cursor; }))
out += *(cursor++);
};
auto parse_delimited = [&](char lhs, std::string &out, char rhs) -> void {
skip_whitespace();
expect_consume(lhs);
parse_until({rhs}, out);
expect_consume(rhs);
};
auto parse_value = [&](std::string &out) -> void {
skip_whitespace();
bool quoted = false;
unsigned depth = 0;
while (true) {
if (cursor == end) throw "unexpected end of stream";
if (is_whitespace(*cursor) && !quoted && depth == 0)
break;
if (depth == 0 && (*cursor == ',' || *cursor == '}'))
break;
out += *(cursor++);
if (out.back() == '{')
++depth;
else if (out.back() == '}')
--depth;
else if (out.back() == '"') {
if (quoted && depth == 0)
break;
quoted = true;
}
};
};
skip_whitespace();
expect_consume('{');
std::vector<std::meta::info> members;
std::vector<std::meta::info> values = {^^void};
using std::meta::reflect_constant, std::meta::reflect_constant_string;
while (cursor != end && *cursor != '}') {
std::string field_name;
std::string value;
std::meta::info parsed_type;
parse_delimited('"', field_name, '"');
expect_consume(':');
parse_value(value);
if (value.empty()) throw "expected value";
if (cursor == end) throw "unexpected end of stream";
if (value[0] == '"') {
if (value.back() == '}' && value[1] == 'f') throw "hmm";
if (value.back() != '"') throw "expected end of string";
std::string_view contents(&value[1], value.size() - 2);
auto dms = data_member_spec(^^char const*, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant_string(contents));
} else if (value[0] >= '0' && value[0] <= '9') {
int contents = [](std::string_view in) {
int out;
std::from_chars(in.data(), in.data() + in.size(), out);
return out;
}(value);
auto dms = data_member_spec(^^int, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(contents));
} else if (value[0] == '{') {
std::meta::info parsed = parse_json(value);
auto dms = data_member_spec(type_of(parsed), {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(parsed);
}
skip_whitespace();
if (cursor != end && *cursor == ',')
++cursor;
}
if (cursor == end) throw "hmm";
expect_consume('}');
values[0] = substitute(^^Cls, members);
return substitute(^^construct_from, values);
}
struct JSONString {
std::meta::info Rep;
consteval JSONString(const char *Json) : Rep{parse_json(Json)} {}
};
template <JSONString json>
consteval auto operator""_json() {
return [:json.Rep:];
}
template <JSONString json>
inline constexpr auto json_to_object = [: json.Rep :];
#include <print>
constexpr const char data[] = {
#embed "test.json"
, 0
};
int main() {
constexpr auto v = json_to_object<data>;
static_assert(std::string_view(v.outer) == "text");
static_assert(v.inner.number == 2996);
static_assert(std::string_view(v.inner.field) == "yes");
std::println("field: {}, number: {}", v.inner.field, v.inner.number);
static_assert(R"({"field": "yes", "number": 2996})"_json.number == 2996);
}
```
where test.json is:
```
{
"outer": "text",
"inner": { "field": "yes", "number": 2996 }
}
```
crashes:
$ xg++ -c -std=c++26 -freflection -save-temps ice1.C
ice1.C: In function ‘int main()’:
in ‘constexpr’ expansion of ‘JSONString(((const char*)(& data)))’
ice1.C:154:41:
154 | constexpr auto v = json_to_object<data>;
| ^
in ‘constexpr’ expansion of ‘parse_json(std::basic_string_view<char>(Json))’
ice1.C:135:60:
135 | consteval JSONString(const char *Json) : Rep{parse_json(Json)} {}
| ~~~~~~~~~~^~~~~~
ice1.C:130:20: internal compiler error: tree check: expected record_type or
union_type or qual_union_type, have template_type_parm in lookup_base, at
cp/search.cc:265
130 | return substitute(^^construct_from, values);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
0x317d3aa internal_error(char const*, ...)
/home/mpolacek/src/gcc/gcc/diagnostic-global-context.cc:787
0x1734565 tree_check_failed(tree_node const*, char const*, int, char const*,
...)
/home/mpolacek/src/gcc/gcc/tree.cc:9204
0x43e5ff tree_check3(tree_node*, char const*, int, char const*, tree_code,
tree_code, tree_code)
/home/mpolacek/src/gcc/gcc/tree.h:3805
0x880371 lookup_base(tree_node*, tree_node*, int, base_kind*, int, long)
/home/mpolacek/src/gcc/gcc/cp/search.cc:265
0x4138bd build_user_type_conversion_1
/home/mpolacek/src/gcc/gcc/cp/call.cc:4602
0x40af88 implicit_conversion
/home/mpolacek/src/gcc/gcc/cp/call.cc:2301
0x415420 build_converted_constant_expr_internal
/home/mpolacek/src/gcc/gcc/cp/call.cc:4970
0x415841 build_converted_constant_expr(tree_node*, tree_node*, int)
/home/mpolacek/src/gcc/gcc/cp/call.cc:5097
0x7bec76 convert_nontype_argument
/home/mpolacek/src/gcc/gcc/cp/pt.cc:7678
0x7c41b5 convert_template_argument
/home/mpolacek/src/gcc/gcc/cp/pt.cc:9013
0x7c4a81 coerce_template_parameter_pack
/home/mpolacek/src/gcc/gcc/cp/pt.cc:9155
0x7c5bec coerce_template_parms(tree_node*, tree_node*, tree_node*, int, bool)
/home/mpolacek/src/gcc/gcc/cp/pt.cc:9393
0x7cb866 lookup_template_variable(tree_node*, tree_node*, int)
/home/mpolacek/src/gcc/gcc/cp/pt.cc:10713
0x867d2c eval_can_substitute
/home/mpolacek/src/gcc/gcc/cp/reflect.cc:5336
0x867e2f eval_substitute
/home/mpolacek/src/gcc/gcc/cp/reflect.cc:5369
0x8755f4 process_metafunction(constexpr_ctx const*, tree_node*, tree_node*,
bool*, bool*, tree_node**)
/home/mpolacek/src/gcc/gcc/cp/reflect.cc:7672
0x491112 cxx_eval_call_expression
/home/mpolacek/src/gcc/gcc/cp/constexpr.cc:3867
0x4a8e9c cxx_eval_constant_expression(constexpr_ctx const*, tree_node*,
value_cat, bool*, bool*, tree_node**)
/home/mpolacek/src/gcc/gcc/cp/constexpr.cc:9205
0x4a22c7 cxx_eval_store_expression
/home/mpolacek/src/gcc/gcc/cp/constexpr.cc:7660
0x4a9c66 cxx_eval_constant_expression(constexpr_ctx const*, tree_node*,
value_cat, bool*, bool*, tree_node**)
/home/mpolacek/src/gcc/gcc/cp/constexpr.cc:9377