https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58822

--- Comment #29 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Michi Henning from comment #27)
> So, this is UB. Really nasty, in the sense that
> 
> make_shared<T>(args) doesn't always do the same thing as shared_ptr<T>(new
> T(args))

It does do effectively the same thing. The difference in behaviour is because
when you use shared_ptr<T>(new T(this)) the implicit cast happens inside the
class, and the compiler can resolve it without inspecting the vtable.

When you use make_shared the implicit cast happens deep inside a nested
function and so it inspects the vtable, which is not yet valid because the base
object isn't constructed, so trying to access the vtable crashes.

You can cause the same crash by doing:

class InvalidArgumentException;
Exception* cast(InvalidArgumentException*);

class InvalidArgumentException : public virtual Exception
{
public:
    explicit InvalidArgumentException(std::string const&)

        : Exception(std::shared_ptr<ExceptionImplBase>(new
ExceptionImplBase(cast(this))))
    {
    }

    virtual ~InvalidArgumentException() noexcept {}
};

Exception* cast(InvalidArgumentException* p) { return p; }


The function 'cast' moves the implicit cast into a separate function, which
reproduces the same crash.

It should be possible to add a warning for the case where the cast happens
inside the class (which doesn't crash, but is still UB) but it won't be
possible to warn for the other cases, because at the site of the implicit cast
the compiler doesn't know whether the pointer refers to a constructed object or
not. I've created PR 70644.

Reply via email to