Skip to content

Clang omits call to move constructor with aggregate init from template type #61145

Closed
llvm/llvm-project-release-prs
#407
@danakj

Description

@danakj

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

No type

Projects

Status

Done

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions