D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 15328 - Postblit not called
Summary: Postblit not called
Status: RESOLVED INVALID
Alias: None
Product: D
Classification: Unclassified
Component: druntime (show other issues)
Version: D2
Hardware: All All
: P1 major
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-11-13 16:59 UTC by Andrei Alexandrescu
Modified: 2015-11-13 21:10 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Andrei Alexandrescu 2015-11-13 16:59:33 UTC
I have a rather complicated repro that could use fresh eyes for simplification. The gist of it is the the function Range save() { return this; } does not call the postblit.

struct List(T)
{
    import std.traits;

    private struct Node
    {
        T payload;
        Node* next;
        uint refs;
    }
    
    // layout {
    private const(Node)* root;
    // } layout
    
    private @trusted static void incRef(const Node* n)
    {
        if (n) ++*(cast(uint*) &(n.refs));
    }
    
    private @trusted static void decRefUnchecked(const Node* n)
    {
        assert(n && n.refs > 1);
        --*(cast(uint*) &(n.refs));
    }
    
    // Entry point, decRef and destroy+deallocate if down to zero
    private static void decRef(const Node* n)
    {
        assert(n);
        if (n.refs > 1)
        {
            decRefUnchecked(n);
            return;
        }
        static if (hasMember!(T, "__dtor"))
            n.payload.__dtor();
    }
    
    this(this)
    {
        incRef(root);
    }
    
    ~this()
    {
        for (auto n = root; n;)
        {
            if (n.refs > 1)
            {
                decRefUnchecked(n);
                return;
            }
            auto goner = n;
            n = n.next;
            static if (hasMember!(T, "__dtor"))
                goner.payload.__dtor();
        }
    }
    
    private this(const Node* n)
    {
        root = n;
    }

    /**
     * Returns a new $(D List) consisting of $(D head) followed by the contents of $(D this).
     * Complexity: $(BIGOH n).
     */
    List opBinaryRight(string op)(T head)
        if (op == "~")
    {
        List result;
        import std.conv : emplace;
        auto n = new const Node(head, root, 1);
        incRef(root);
        result.root = n;
        return result;
    }

    auto opSlice() inout
    {
        static struct Range
        {
            // layout {
            private const(Node)* root;
            // } layout 
            
            this(this)
            {
                incRef(root);
            }
            
            ~this()
            {
                if (!root) return;
                assert(root.refs >= 1);
                decRef(root);
            }
            
            Range save()
            {
                return this;
            }
        }
        incRef(root);
        auto result = Range(root);
        return result;
    }
}

unittest
{
    auto lst = 1 ~ (2 ~ (3 ~ List!(immutable int)()));
    auto lst2 = 42 ~ lst;
    auto range = lst2[];
    assert(lst.root.refs == 2);
    auto range1 = range.save;
    assert(lst.root.refs == 3); // fails
}

void main() {}
Comment 1 Dicebot 2015-11-13 17:13:13 UTC
Can it be RVO hitting?
Comment 2 Andrei Alexandrescu 2015-11-13 17:27:01 UTC
(In reply to Dicebot from comment #1)
> Can it be RVO hitting?

If it is it's misapplied - the example is reduced from a bug that frees memory too early.
Comment 3 Andrei Alexandrescu 2015-11-13 17:39:41 UTC
Looks like I wrongly used lst instead of lst2 in this example. Will get back soon.
Comment 4 Andrei Alexandrescu 2015-11-13 21:10:06 UTC
Forgot an incRef in popFront. The bug is invalid - sorry for the distraction!