mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 09:16:38 +00:00

We used to assume that the CXXRecordDecl passed to the 1st argument always had a definition. This is not true since a pointer to an incomplete type was not excluded. Fixes https://github.com/llvm/llvm-project/issues/63506
199 lines
6.8 KiB
C++
199 lines
6.8 KiB
C++
// 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]);
|
|
|
|
class Incomplete; // #incomplete-type
|
|
|
|
template <class T>
|
|
class Class {
|
|
T value = {};
|
|
};
|
|
|
|
void errors(B b) {
|
|
ConstexprString cs;
|
|
__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}}
|
|
__builtin_dump_struct((Incomplete *)nullptr, Format, cs); // expected-error {{incomplete type 'Incomplete' where a complete type is required}}
|
|
// expected-note@#incomplete-type {{forward declaration of 'Incomplete'}}
|
|
// Ensure the Class<int> gets instantiated; otherwise crash happens.
|
|
__builtin_dump_struct((Class<int> *)nullptr, Format, cs);
|
|
}
|
|
#endif
|
|
|
|
// Check that PseudoObjectExprBitfields:NumSubExprs doesn't overflow. This
|
|
// would previously cause a crash.
|
|
struct t1 {
|
|
int v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16,
|
|
v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31,
|
|
v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46,
|
|
v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61,
|
|
v62, v63, v64, v65, v66, v67, v68, v69, v70, v71, v72, v73, v74, v75, v76,
|
|
v77, v78, v79, v80, v81, v82, v83, v84, v85, v86, v87, v88, v89, v90, v91,
|
|
v92, v93, v94, v95, v96, v97, v98, v99;
|
|
};
|
|
|
|
struct t2 {
|
|
t1 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16,
|
|
v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31,
|
|
v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46,
|
|
v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61,
|
|
v62, v63, v64, v65, v66, v67, v68, v69, v70, v71, v72, v73, v74, v75, v76,
|
|
v77, v78, v79, v80, v81, v82, v83, v84, v85, v86, v87, v88, v89, v90, v91,
|
|
v92, v93, v94, v95, v96, v97, v98, v99;
|
|
};
|
|
|
|
int printf(const char *, ...);
|
|
void f1(t2 w) { __builtin_dump_struct(&w, printf); }
|