blob: e057eac029463d3554796a5bf0274b4bcd8fbc30 [file] [log] [blame]
// 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