Thread overview
Presenting the Turducken Type Technique: Certified Evil™
Jul 11, 2018
FeepingCreature
Jul 11, 2018
FeepingCreature
Jul 11, 2018
Anton Fediushin
July 11, 2018
// So I was working on Nullable,
// and I thought: how could this be made to work better?
// The following code runs on DMD master,
// and it is, technically, `safe`;
// but it REALLY REALLY shouldn't be.
module turducken;

import std.algorithm;
import std.datetime;

// The Turducken Type Technique is the answer to a challenge.
// How do we perform late binding and early unbinding
// on a type that's really, really evil?

// First of all, let's set the stage.
// We're going to construct the Type from Hell.
// It's a struct value type, giving us the
// greatest number of avenues to be horrible.
struct Satan
{
    // It has an immutable value field, referencing immutable data.
    immutable int[] i;

    // It has a SysTime: it's caused issues for
    // me in the past and I hold a grudge.
    SysTime st;

    // It has no default constructor.
    @disable this();

    // It has an .init that violates its invariant.
    bool properlyInitialized = false;

    invariant { assert(properlyInitialized); }

    // It has a copy assignment overload,
    // specifically so we can forbid copy assignment.
    void opAssign(Satan) { assert(false); }

    // To confirm that every constructor call matches one destructor call,
    // it counts references.
    int *refs;

    this(int* refs)
    pure @safe @nogc
    {
        this.properlyInitialized = true;
        this.refs = refs;
        (*refs)++;
    }

    this(this)
    pure @safe @nogc
    { (*refs)++; }

    // Since a destructor is defined, we will definitely
    // assert out if the .init value is ever destructed.
    ~this()
    pure @safe @nogc
    in(refs)
    out(; *refs >= 0)
    do { (*refs)--; }
}

// Now, this is the challenge.

// In a function that is
pure // pure,
@safe // safe,
@nogc // and nogc:
unittest
{
    // perform late binding and early unbinding of the Type from Hell.

    // Let's do some prep.

    // (for validation)
    int refs;

    // (cheat a bit)
    int* refsp = () @trusted { return &refs; }();

    {
        // We start with a default initialized variable.
        Turducken!Satan magic;

        // we bind it to a constructed value
        magic.bind(Satan(refsp));

        // There's now exactly one copy of Satan in the world:
        assert(refs == 1);

        // Just for laughs, bind over it:
        magic.bind(Satan(refsp));

        // Still only one copy around.
        assert(refs == 1);

        // all is well with the contained value
        assert(magic.value.properlyInitialized);

        // Now we unbind it before scope end
        magic.unbind;

        // Satan is gone now.
        assert(refs == 0);
    }
    // And he was destroyed exactly once, as is proper.
    assert(refs == 0);
}

// How did we do this?
// Turducken!
struct Turducken(T)
{
    // The delicious center.
    alias Chicken = T;

    // Union ensures that the T destructor is not called.
    union Duck
    {
        Chicken chicken; // chicken chicken? chicken!
    }

    // Struct ensures that moveEmplace and its magic
    // constness "tolerant" (violating) memcpy can be used.
    struct Turkey
    {
        Duck duck;
    }

    Turkey store = Turkey.init; // take you to the turkey store

    bool set = false;

    // Stick a straw in it to get at the delicious gooey center.
    @property ref T value() in(set) { return store.duck.chicken; }

    // And the special sauce:
    void bind(T value)
    {
        // clean up in case we're back for seconds
        unbind;

        // Make a destructor-protected copy to stick in our store
        Turkey wrapper = Turkey(Duck(value));

        // Plow over the existing data in total disregard of constness
        () @trusted { moveEmplace(wrapper, store); }();

        set = true;
    }

    // Various condiments:
    void unbind()
    {
        if (set)
        {
            static if (is(T == struct)) {
                destroy(value);
            }
            set = false;
        }
    }

    // Since we have shielded our value from D's watchful eyes,
    // we have to remember to clean up manually
    // Clean your dishes!
    ~this()
    {
        unbind;
    }
}

// And that's the Turducken Type Technique. Deliciously evil!

July 11, 2018
Oh yeah, two notes.

First: you might say "it's easy to be un@safe if you use @trusted." However, the @trusted part is both well-defined and positively pedestrian, since we are either targeting default initialized memory or just ran destroy() on it (which by the way is @safe) so really, if you're holding a reference past that *you're* the one doing undefined things.

Second: since I forgot, credit @n8sh on github for pointing me at the union technique, though the blame for the current incarnation of the trick is all mine.
July 11, 2018
On Wednesday, 11 July 2018 at 18:36:24 UTC, FeepingCreature wrote:
> // So I was working on Nullable,
> // and I thought: how could this be made to work better?
> // The following code runs on DMD master,
> // and it is, technically, `safe`;
> // but it REALLY REALLY shouldn't be.
[...]
> // And that's the Turducken Type Technique. Deliciously evil!

That's a thing of beauty. I really want to try this in one of my projects.