Compiler projects using llvm
// RUN: %clang_cc1 -std=c++20 -verify %s

namespace std {
  typedef decltype(sizeof(int)) size_t;

  template <class E> struct initializer_list {
    const E *data;
    size_t size;

    constexpr initializer_list(const E *data, size_t size)
        : data(data), size(size) {}
    constexpr initializer_list() : data(), size() {}

    constexpr const E *begin() const { return data; }
    constexpr const E *end() const { return data + size; }
  };
}

struct ConstexprString {
  constexpr ConstexprString() : ConstexprString("") {}
  constexpr ConstexprString(const char *p, std::size_t size) : data(new char[size+1]) {
    __builtin_memcpy(data, p, size);
    data[size] = '\0';
  }
  constexpr ConstexprString(const char *p) : ConstexprString(p, __builtin_strlen(p)) {}
  constexpr explicit ConstexprString(const char *p, const char *q) : data(nullptr) {
    auto p_size = __builtin_strlen(p);
    auto q_size = __builtin_strlen(q);
    data = new char[p_size + q_size + 1];
    __builtin_memcpy(data, p, p_size);
    __builtin_memcpy(data + p_size, q, q_size + 1);
  }
  constexpr ConstexprString(const ConstexprString &o) : ConstexprString(o.data) {}
  constexpr ConstexprString(ConstexprString &&o) : data(o.data) { o.data = nullptr; }
  constexpr ConstexprString &operator=(const ConstexprString &o) {
    return *this = ConstexprString(o);
  }
  constexpr ConstexprString &operator=(ConstexprString &&o) {
    delete[] data;
    data = o.data;
    o.data = nullptr;
    return *this;
  }
  constexpr ~ConstexprString() { delete[] data; }
  char *data;

  friend constexpr ConstexprString operator+(const ConstexprString &a, const ConstexprString &b) {
    return ConstexprString(a.data, b.data);
  }
  friend constexpr ConstexprString &operator+=(ConstexprString &a, const ConstexprString &b) {
    return a = a + b;
  }
  friend constexpr bool operator==(const ConstexprString &a, const ConstexprString &b) {
    return __builtin_strcmp(a.data, b.data) == 0;
  }
};

template<typename... T> constexpr void Format(ConstexprString &out, const char *fmt, T... args);

struct Arg {
  template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr>
  constexpr Arg(T value) {
    bool negative = false;
    if (value < 0) {
      value = -value;
      negative = true;
    }
    while (value > 0) {
      char str[2] = {char('0' + value % 10), '\0'};
      s = ConstexprString(str) + s;
      value /= 10;
    }
    if (negative)
      s = "-" + s;
  }
  template<typename T, int (*)[__is_class(T) ? 1 : -1] = nullptr>
  constexpr Arg(const T &value) {
    __builtin_dump_struct(&value, Format, s);
  }
  constexpr Arg(const char *s) : s(s) {}
  constexpr Arg(const ConstexprString *s) : s("\"" + *s + "\"") {}
  template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr>
  constexpr Arg(const T *p) : s("reference to " + Arg(*p).s) {}
  ConstexprString s;
};

template<typename... T> constexpr void Format(ConstexprString &out, const char *fmt, T... args) { // #Format
  Arg formatted_args[] = {args...};
  int i = 0;
  while (const char *percent = __builtin_strchr(fmt, '%')) {
    if (percent[1] == '%') continue;
    if (percent != fmt && percent[-1] == '*') --percent;
    out += ConstexprString(fmt, percent - fmt);
    out += formatted_args[i++].s;

    // Skip past format specifier until we hit a conversion specifier.
    fmt = percent;
    while (!__builtin_strchr("diouxXeEfFgGcsp", *fmt)) ++fmt;
    // Skip the conversion specifier too. TODO: Check it's the right one.
    ++fmt;
  }
  out += ConstexprString(fmt);
}

template<typename T> constexpr ConstexprString ToString(const T &t) { return Arg(t).s; }

struct A {
  int x, y, z : 3;
  int : 4;
  ConstexprString s;
};
struct B : A {
  int p, q;
  struct {
    int anon1, anon2;
  };
  union {
    int anon3;
  };
  struct {
    int m;
  } c;
  int &&r;
};

#if PRINT_OUTPUT
#include <stdio.h>
int main() {
  puts(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8, 9, 10}).data);
}
#else
static_assert(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8, 9, 10}) == &R"(
B {
  A {
    int x = 1
    int y = 2
    int z : 3 = 3
    ConstexprString s = "hello"
  }
  int p = 4
  int q = 5
  int anon1 = 6
  int anon2 = 7
  int anon3 = 8
  struct (unnamed) c = {
    int m = 9
  }
  int && r = reference to 10
}
)"[1]);

void errors(B b) {
  __builtin_dump_struct(); // expected-error {{too few arguments to function call, expected 2, have 0}}
  __builtin_dump_struct(1); // expected-error {{too few arguments to function call, expected 2, have 1}}
  __builtin_dump_struct(1, 2); // expected-error {{expected pointer to struct as 1st argument to '__builtin_dump_struct', found 'int'}}
  __builtin_dump_struct(&b, 2); // expected-error {{expected a callable expression as 2nd argument to '__builtin_dump_struct', found 'int'}}
  __builtin_dump_struct(&b, Format, 0); // expected-error {{no matching function for call to 'Format'}}
                                        // expected-note@-1 {{in call to printing function with arguments '(0, "%s", "B")' while dumping struct}}
                                        // expected-note@#Format {{no known conversion from 'int' to 'ConstexprString &' for 1st argument}}
}
#endif