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