1 // RUN: %clang_cc1 -std=c++20 -verify %s 2 3 namespace std { 4 typedef decltype(sizeof(int)) size_t; 5 6 template <class E> struct initializer_list { 7 const E *data; 8 size_t size; 9 10 constexpr initializer_list(const E *data, size_t size) 11 : data(data), size(size) {} 12 constexpr initializer_list() : data(), size() {} 13 14 constexpr const E *begin() const { return data; } 15 constexpr const E *end() const { return data + size; } 16 }; 17 } 18 19 struct ConstexprString { 20 constexpr ConstexprString() : ConstexprString("") {} 21 constexpr ConstexprString(const char *p, std::size_t size) : data(new char[size+1]) { 22 __builtin_memcpy(data, p, size); 23 data[size] = '\0'; 24 } 25 constexpr ConstexprString(const char *p) : ConstexprString(p, __builtin_strlen(p)) {} 26 constexpr explicit ConstexprString(const char *p, const char *q) : data(nullptr) { 27 auto p_size = __builtin_strlen(p); 28 auto q_size = __builtin_strlen(q); 29 data = new char[p_size + q_size + 1]; 30 __builtin_memcpy(data, p, p_size); 31 __builtin_memcpy(data + p_size, q, q_size + 1); 32 } 33 constexpr ConstexprString(const ConstexprString &o) : ConstexprString(o.data) {} 34 constexpr ConstexprString(ConstexprString &&o) : data(o.data) { o.data = nullptr; } 35 constexpr ConstexprString &operator=(const ConstexprString &o) { 36 return *this = ConstexprString(o); 37 } 38 constexpr ConstexprString &operator=(ConstexprString &&o) { 39 delete[] data; 40 data = o.data; 41 o.data = nullptr; 42 return *this; 43 } 44 constexpr ~ConstexprString() { delete[] data; } 45 char *data; 46 47 friend constexpr ConstexprString operator+(const ConstexprString &a, const ConstexprString &b) { 48 return ConstexprString(a.data, b.data); 49 } 50 friend constexpr ConstexprString &operator+=(ConstexprString &a, const ConstexprString &b) { 51 return a = a + b; 52 } 53 friend constexpr bool operator==(const ConstexprString &a, const ConstexprString &b) { 54 return __builtin_strcmp(a.data, b.data) == 0; 55 } 56 }; 57 58 template<typename... T> constexpr void Format(ConstexprString &out, const char *fmt, T... args); 59 60 struct Arg { 61 template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr> 62 constexpr Arg(T value) { 63 bool negative = false; 64 if (value < 0) { 65 value = -value; 66 negative = true; 67 } 68 while (value > 0) { 69 char str[2] = {char('0' + value % 10), '\0'}; 70 s = ConstexprString(str) + s; 71 value /= 10; 72 } 73 if (negative) 74 s = "-" + s; 75 } 76 template<typename T, int (*)[__is_class(T) ? 1 : -1] = nullptr> 77 constexpr Arg(const T &value) { 78 __builtin_dump_struct(&value, Format, s); 79 } 80 constexpr Arg(const char *s) : s(s) {} 81 constexpr Arg(const ConstexprString *s) : s("\"" + *s + "\"") {} 82 template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr> 83 constexpr Arg(const T *p) : s("reference to " + Arg(*p).s) {} 84 ConstexprString s; 85 }; 86 87 template<typename... T> constexpr void Format(ConstexprString &out, const char *fmt, T... args) { // #Format 88 Arg formatted_args[] = {args...}; 89 int i = 0; 90 while (const char *percent = __builtin_strchr(fmt, '%')) { 91 if (percent[1] == '%') continue; 92 if (percent != fmt && percent[-1] == '*') --percent; 93 out += ConstexprString(fmt, percent - fmt); 94 out += formatted_args[i++].s; 95 96 // Skip past format specifier until we hit a conversion specifier. 97 fmt = percent; 98 while (!__builtin_strchr("diouxXeEfFgGcsp", *fmt)) ++fmt; 99 // Skip the conversion specifier too. TODO: Check it's the right one. 100 ++fmt; 101 } 102 out += ConstexprString(fmt); 103 } 104 105 template<typename T> constexpr ConstexprString ToString(const T &t) { return Arg(t).s; } 106 107 struct A { 108 int x, y, z : 3; 109 int : 4; 110 ConstexprString s; 111 }; 112 struct B : A { 113 int p, q; 114 struct { 115 int anon1, anon2; 116 }; 117 union { 118 int anon3; 119 }; 120 struct { 121 int m; 122 } c; 123 int &&r; 124 }; 125 126 #if PRINT_OUTPUT 127 #include <stdio.h> 128 int main() { 129 puts(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8, 9, 10}).data); 130 } 131 #else 132 static_assert(ToString(B{1, 2, 3, "hello", 4, 5, 6, 7, 8, 9, 10}) == &R"( 133 B { 134 A { 135 int x = 1 136 int y = 2 137 int z : 3 = 3 138 ConstexprString s = "hello" 139 } 140 int p = 4 141 int q = 5 142 int anon1 = 6 143 int anon2 = 7 144 int anon3 = 8 145 struct (unnamed) c = { 146 int m = 9 147 } 148 int && r = reference to 10 149 } 150 )"[1]); 151 152 void errors(B b) { 153 __builtin_dump_struct(); // expected-error {{too few arguments to function call, expected 2, have 0}} 154 __builtin_dump_struct(1); // expected-error {{too few arguments to function call, expected 2, have 1}} 155 __builtin_dump_struct(1, 2); // expected-error {{expected pointer to struct as 1st argument to '__builtin_dump_struct', found 'int'}} 156 __builtin_dump_struct(&b, 2); // expected-error {{expected a callable expression as 2nd argument to '__builtin_dump_struct', found 'int'}} 157 __builtin_dump_struct(&b, Format, 0); // expected-error {{no matching function for call to 'Format'}} 158 // expected-note@-1 {{in call to printing function with arguments '(0, "%s", "B")' while dumping struct}} 159 // expected-note@#Format {{no known conversion from 'int' to 'ConstexprString &' for 1st argument}} 160 } 161 #endif 162