Description
cc: @alanzhao1
This code double frees with Clang, when it should not: https://godbolt.org/z/5sfT8sW4j
#include <iostream>
#include <utility>
struct VecIter;
struct Vec {
Vec() : i(new int[3]) {
std::cerr << this << " Vec::Vec()\n";
i[0] = 1;
i[1] = 2;
i[2] = 3;
}
Vec(Vec&& o) : i(o.i) {
std::cerr << this << " Vec::Vec&&\n";
o.i = nullptr;
}
Vec& operator=(Vec&& o) {
std::cerr << this << " Vec::operator=&&\n";
delete[] i;
i = o.i;
o.i = nullptr;
return *this;
}
~Vec() {
std::cerr << this << " Vec::~Vec\n";
delete[] i;
}
int* i;
};
struct S {
static S make(auto&& vec) {
Vec v;
std::cerr << "Building Vec for S " << &v << "\n";
v.i[0] = vec.i[0];
v.i[1] = vec.i[1];
v.i[2] = vec.i[2];
std::cerr << "Construct S from Vec " << &v << "\n";
// Clang bug:
// The Vec::Vec(Vec&&) constructor is omitted here when constructing S
// and S::vec.
//
// * This does not happen if the parameter is `Vec&&` instead of `auto&&`.
// * It also does not happen if we uncomment the explicit constructor for S
// below.
return S(std::move(v));
}
// Defining this constructor fixes the bug in S::make() and causes the Vec
// move constructor to be called.
// S(Vec vec) : vec(std::move(vec)) {}
Vec vec;
};
int main() {
auto s = S::make(Vec());
std::cerr << s.vec.i[0] << "\n";
std::cerr << s.vec.i[1] << "\n";
std::cerr << s.vec.i[2] << "\n";
}
Output in GCC:
0x7ffe7c534788 Vec::Vec()
0x7ffe7c534758 Vec::Vec()
Building Vec for S 0x7ffe7c534758
Construct S from Vec 0x7ffe7c534758
0x7ffe7c534780 Vec::Vec&&
0x7ffe7c534758 Vec::~Vec
0x7ffe7c534788 Vec::~Vec
1
2
3
0x7ffe7c534780 Vec::~Vec
Output in Clang:
0x7ffe481779b0 Vec::Vec()
0x7ffe48177958 Vec::Vec()
Building Vec for S 0x7ffe48177958
Construct S from Vec 0x7ffe48177958
0x7ffe48177958 Vec::~Vec
0x7ffe481779b0 Vec::~Vec
0
0
493486096
0x7ffe481779b8 Vec::~Vec
free(): double free detected in tcache 2
Can't show the output of MSVC from godbolt but the code works there.
Metadata
Metadata
Assignees
Type
Projects
Status
Done
Status
Done