aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite/g++.dg/torture/pr71002.C
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/testsuite/g++.dg/torture/pr71002.C')
-rw-r--r--gcc/testsuite/g++.dg/torture/pr71002.C160
1 files changed, 160 insertions, 0 deletions
diff --git a/gcc/testsuite/g++.dg/torture/pr71002.C b/gcc/testsuite/g++.dg/torture/pr71002.C
new file mode 100644
index 00000000000..8a726809217
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr71002.C
@@ -0,0 +1,160 @@
+// { dg-do run }
+
+using size_t = __SIZE_TYPE__;
+
+inline void* operator new(size_t, void* p) noexcept
+{ return p; }
+
+inline void operator delete(void*, void*)
+{ }
+
+struct long_t
+{
+ size_t is_short : 1;
+ size_t length : (__SIZEOF_SIZE_T__ * __CHAR_BIT__ - 1);
+ size_t capacity;
+ char* pointer;
+};
+
+union long_raw_t {
+ unsigned char data[sizeof(long_t)];
+ struct __attribute__((aligned(alignof(long_t)))) { } align;
+};
+
+struct short_header
+{
+ unsigned char is_short : 1;
+ unsigned char length : (__CHAR_BIT__ - 1);
+};
+
+struct short_t
+{
+ short_header h;
+ char data[23];
+};
+
+union repr_t
+{
+ long_raw_t r;
+ short_t s;
+
+ const short_t& short_repr() const
+ { return s; }
+
+ const long_t& long_repr() const
+ { return *static_cast<const long_t*>(static_cast<const void*>(&r)); }
+
+ short_t& short_repr()
+ { return s; }
+
+ long_t& long_repr()
+ { return *static_cast<long_t*>(static_cast<void*>(&r)); }
+};
+
+class string
+{
+public:
+ string()
+ {
+ short_t& s = m_repr.short_repr();
+ s.h.is_short = 1;
+ s.h.length = 0;
+ s.data[0] = '\0';
+ }
+
+ string(const char* str)
+ {
+ size_t length = __builtin_strlen(str);
+ if (length + 1 > 23) {
+ long_t& l = m_repr.long_repr();
+ l.is_short = 0;
+ l.length = length;
+ l.capacity = length + 1;
+ l.pointer = new char[l.capacity];
+ __builtin_memcpy(l.pointer, str, length + 1);
+ } else {
+ short_t& s = m_repr.short_repr();
+ s.h.is_short = 1;
+ s.h.length = length;
+ __builtin_memcpy(s.data, str, length + 1);
+ }
+ }
+
+ string(string&& other)
+ : string{}
+ {
+ swap_data(other);
+ }
+
+ ~string()
+ {
+ if (!is_short()) {
+ delete[] m_repr.long_repr().pointer;
+ }
+ }
+
+ size_t length() const
+ { return is_short() ? short_length() : long_length(); }
+
+private:
+ bool is_short() const
+ { return m_repr.s.h.is_short != 0; }
+
+ size_t short_length() const
+ { return m_repr.short_repr().h.length; }
+
+ size_t long_length() const
+ { return m_repr.long_repr().length; }
+
+ void swap_data(string& other)
+ {
+ if (is_short()) {
+ if (other.is_short()) {
+ repr_t tmp(m_repr);
+ m_repr = other.m_repr;
+ other.m_repr = tmp;
+ } else {
+ short_t short_backup(m_repr.short_repr());
+ m_repr.short_repr().~short_t();
+ ::new(&m_repr.long_repr()) long_t(other.m_repr.long_repr());
+ other.m_repr.long_repr().~long_t();
+ ::new(&other.m_repr.short_repr()) short_t(short_backup);
+ }
+ } else {
+ if (other.is_short()) {
+ short_t short_backup(other.m_repr.short_repr());
+ other.m_repr.short_repr().~short_t();
+ ::new(&other.m_repr.long_repr()) long_t(m_repr.long_repr());
+ m_repr.long_repr().~long_t();
+ ::new(&m_repr.short_repr()) short_t(short_backup);
+ } else {
+ long_t tmp(m_repr.long_repr());
+ m_repr.long_repr() = other.m_repr.long_repr();
+ other.m_repr.long_repr() = tmp;
+ }
+ }
+ }
+
+ repr_t m_repr;
+};
+
+struct foo
+{
+ __attribute__((noinline))
+ foo(string str)
+ : m_str{static_cast<string&&>(str)},
+ m_len{m_str.length()}
+ { }
+
+ string m_str;
+ size_t m_len;
+};
+
+int main()
+{
+ foo f{"the quick brown fox jumps over the lazy dog"};
+ if (f.m_len == 0) {
+ __builtin_abort();
+ }
+ return 0;
+}