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
initializer_liststd::initializer_list10 constexpr initializer_list(const E *data, size_t size)
11 : data(data), size(size) {}
initializer_liststd::initializer_list12 constexpr initializer_list() : data(), size() {}
13
beginstd::initializer_list14 constexpr const E *begin() const { return data; }
endstd::initializer_list15 constexpr const E *end() const { return data + size; }
16 };
17 }
18
19 struct ConstexprString {
ConstexprStringConstexprString20 constexpr ConstexprString() : ConstexprString("") {}
ConstexprStringConstexprString21 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 }
ConstexprStringConstexprString25 constexpr ConstexprString(const char *p) : ConstexprString(p, __builtin_strlen(p)) {}
ConstexprStringConstexprString26 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 }
ConstexprStringConstexprString33 constexpr ConstexprString(const ConstexprString &o) : ConstexprString(o.data) {}
ConstexprStringConstexprString34 constexpr ConstexprString(ConstexprString &&o) : data(o.data) { o.data = nullptr; }
operator =ConstexprString35 constexpr ConstexprString &operator=(const ConstexprString &o) {
36 return *this = ConstexprString(o);
37 }
operator =ConstexprString38 constexpr ConstexprString &operator=(ConstexprString &&o) {
39 delete[] data;
40 data = o.data;
41 o.data = nullptr;
42 return *this;
43 }
~ConstexprStringConstexprString44 constexpr ~ConstexprString() { delete[] data; }
45 char *data;
46
operator +(const ConstexprString & a,const ConstexprString & b)47 friend constexpr ConstexprString operator+(const ConstexprString &a, const ConstexprString &b) {
48 return ConstexprString(a.data, b.data);
49 }
operator +=(ConstexprString & a,const ConstexprString & b)50 friend constexpr ConstexprString &operator+=(ConstexprString &a, const ConstexprString &b) {
51 return a = a + b;
52 }
operator ==(const ConstexprString & a,const ConstexprString & b)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>
ArgArg62 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>
ArgArg77 constexpr Arg(const T &value) {
78 __builtin_dump_struct(&value, Format, s);
79 }
ArgArg80 constexpr Arg(const char *s) : s(s) {}
ArgArg81 constexpr Arg(const ConstexprString *s) : s("\"" + *s + "\"") {}
82 template<typename T, int (*)[__is_integral(T) ? 1 : -1] = nullptr>
ArgArg83 constexpr Arg(const T *p) : s("reference to " + Arg(*p).s) {}
84 ConstexprString s;
85 };
86
Format(ConstexprString & out,const char * fmt,T...args)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
ToString(const T & t)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>
main()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
errors(B b)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