// RUN: %clang_cc1 -std=c++20 -verify %s namespace std { typedef decltype(sizeof(int)) size_t; template 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 constexpr void Format(ConstexprString &out, const char *fmt, T... args); struct Arg { template 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 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 constexpr Arg(const T *p) : s("reference to " + Arg(*p).s) {} ConstexprString s; }; template 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 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 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