Баг или фитча?
От: nen777w  
Дата: 05.09.19 11:07
Оценка: 11 (2)
Добрый час.
  "Есть такой код."
#include <iostream>

class Object
{
public:
    Object()
        : p(nullptr)
    {
        int n = 0;
        n++;
    }

    virtual ~Object()
    {
        if (p) {
            delete p;
        }
    }

private:
    int * p;
};

class IPrimitive : public virtual Object
{

};

class ITrailerable : public virtual Object
{

};

class IStableInfo : public virtual Object
{

};

class Primitive : public virtual IPrimitive, public virtual ITrailerable
{
private:
    int arr[100];
};

class IObject : public virtual Object
{

};

class ISerializable : public virtual Object
{

};

class IntermObject : public Primitive, public virtual IObject, public virtual ISerializable, public IStableInfo
{
public:
    IntermObject()
    {
    }

    IntermObject(int v)
        : IntermObject()
    {
        throw "uups";
    }

    IntermObject(int v, int t)
        : IntermObject(v)
    {
        
    }

    ~IntermObject()
    {
        int n = 0;
        n++;
    }

private:
    int n;
    bool b;
};

class IPage : public virtual ITrailerable
{

};

class IPages : public virtual Object
{

};

class IPageTreeNode : public virtual IObject, public IPage, public IPages
{

};

class PageTreeNode final : public IntermObject, public IPageTreeNode
{
public:
    PageTreeNode()
        : IntermObject(10, 20)
    {}
};


int main()
{
    std::cout << "Hello !\n"; 
    
    try
    {
        new PageTreeNode();
    }
    catch (...)
    {[img][/img]
        std::cout << "Catch!\n";
        int n = 0;
        n++;
    }

    std::cout << "World!\n"; 
}


Проблема в том что при выборосе исключения:
IntermObject(int v)
        : IntermObject()
    {
        throw "uups";
    }


В деструкторе Object происходит AV (Windows — cl) или Seg.Fault (Linux — gcc)

  "Из за неправильного смещения:"


Вот такое изменение:
class PageTreeNode final : public IntermObject, public virtual IPageTreeNode


Исправляет проблему с падением, но приводит к двойному вызову деструктора ~Object() (конструктор вызывается 1 раз)
Мне не очень понятно зачем здесь виртуальное наследование, и правы ли компиляторы?

Другой пример:
struct Object
{
    Object() = default;
    virtual ~Object() = default;
};

struct IntermObject : public virtual Object
{
    IntermObject() = default;
    IntermObject(int v) : IntermObject() {}
};

struct PageTreeNode : public IntermObject
{
    PageTreeNode() : IntermObject(1) {}
};

int main()
{
    new PageTreeNode();
}


Уже не приводит к seg. fault у gcc, но приводит к AV у cl (при запуске исполняемого файла).
Замена = default на обычную имплементацию "по старинке" решает проблему с cl.

cl.exe — Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27031.1
gcc — из https://www.onlinegdb.com
Отредактировано 05.09.2019 11:16 nen777w . Предыдущая версия . Еще …
Отредактировано 05.09.2019 11:14 nen777w . Предыдущая версия .
Отредактировано 05.09.2019 11:09 nen777w . Предыдущая версия .
Re: Баг или фитча?
От: _NN_ www.nemerleweb.com
Дата: 05.09.19 12:37
Оценка:
Здравствуйте, nen777w, Вы писали:

GCC 9.2 отрабатывает корректно: https://gcc.godbolt.org/z/pCCLP-
MSVC 16.2 и 16.3 падает.
Нужно запостить баг: https://developercommunity.visualstudio.com/

struct Object
{
    Object() = default;
    virtual ~Object() = default;
};

struct IntermObject : public virtual Object
{
    IntermObject() = default;
    IntermObject(int v) : IntermObject() {}
};

struct PageTreeNode : public IntermObject
{
    PageTreeNode() : IntermObject(1) {}
};

int main()
{
    new PageTreeNode();
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Отредактировано 05.09.2019 12:51 _NN_ . Предыдущая версия .
Re: Баг или фитча?
От: nen777w  
Дата: 05.09.19 13:58
Оценка:
Более короткий пример на первый кейс (коллега подсказал):

struct A
{
    void* p = nullptr;

    virtual ~A()
    {
        assert(p == nullptr);
    }
};

struct B : public virtual A {};

struct C : public virtual A
{
    C() {}
    C(int v) : C() { throw 1; }
};

struct D : public C, public B
{
    D() : C(1) {}
};

int main()
{
    try
    {
        D d;
    }
    catch (int)
    {
    }
    return 0;
}
Re[2]: Баг или фитча?
От: _NN_ www.nemerleweb.com
Дата: 05.09.19 14:12
Оценка:
Здравствуйте, nen777w, Вы писали:

N>Более короткий пример на первый кейс (коллега подсказал):

Пора переходить на clang, там всё собирается как надо и ничего не падает
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Баг или фитча?
От: _NN_ www.nemerleweb.com
Дата: 05.09.19 14:50
Оценка:
Квтати баг репорт есть или открыть ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Баг или фитча?
От: nen777w  
Дата: 05.09.19 16:00
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Квтати баг репорт есть или открыть ?


Откройте пожалуйста. Все никак не дойду до регистрации.
На 19-й студии если что, тоже повторяется и на x86 и на x64.
Отредактировано 05.09.2019 18:53 nen777w . Предыдущая версия .
Re[3]: Баг или фитча?
От: _NN_ www.nemerleweb.com
Дата: 05.09.19 16:38
Оценка: 19 (3)
Здравствуйте, nen777w, Вы писали:

Открыл два разных.
Вдруг окажутся не связанными ошибками.

https://developercommunity.visualstudio.com/content/problem/724662/virtual-inheritance-with-defaulted-constructor-gen.html
https://developercommunity.visualstudio.com/content/problem/724661/virtual-inheritance-generates-invalid-code.html
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Баг или фитча?
От: _NN_ www.nemerleweb.com
Дата: 01.10.19 00:45
Оценка:
Здравствуйте, nen777w, Вы писали:

МС говорят бага нет.
https://developercommunity.visualstudio.com/content/problem/724661/virtual-inheritance-generates-invalid-code.html
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Баг или фитча?
От: _NN_ www.nemerleweb.com
Дата: 09.10.19 14:09
Оценка:
Здравствуйте, nen777w, Вы писали:

N>Более короткий пример на первый кейс (коллега подсказал):


Говорят бага нет.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Баг или фитча?
От: Molchalnik  
Дата: 08.11.19 22:39
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Здравствуйте, nen777w, Вы писали:


N>>Более короткий пример на первый кейс (коллега подсказал):


_NN>Говорят бага нет.


Коллега, по некоторым Вашим вопросам я не могу ответить, но ваш пример в баг трекере имеет следующую особенность: при попытке создания метода D компилер сначала вызывает конструктор A, потом конструктор C, в котором происходит исключение и цепочка вызова конструкторов прерывается — не вызывается конструктор B (потому что он позже наследуется) и D. После исключения вызываются деструкторы в обратном порядке: Деструктор C потом А. Единственное, что я вообще пока не понял, почему деструктор A вообще вызывается дважды. Ведь по идее, благодаря виртуальному наследованию класс A только один, и поэтому и деструктор должен вызываться только один раз. Буду думать. А вот что при втором вызове выводит 0, а не 1 — ничего удивительного: ведь вызывается деструктор уже уничтоженного класса, и в нём может быть что угодно, вероятно компилер зануляет класс.

По той же причине из примера можно выкинуть класс D, и даже класс B. Всё равно они не принимают участие в банкете.

Вот для вдохновения ссыль на код и результаты, чтобы не было сомнений в моих словах
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.