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

Reply via email to