Re: Presenting the Turducken Type Technique: Certified Evil™

2018-07-11 Thread Anton Fediushin via Digitalmars-d

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.


Re: Presenting the Turducken Type Technique: Certified Evil™

2018-07-11 Thread FeepingCreature via Digitalmars-d

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.


Presenting the Turducken Type Technique: Certified Evil™

2018-07-11 Thread FeepingCreature via Digitalmars-d

// 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!