Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions std/algorithm/mutation.d
Original file line number Diff line number Diff line change
Expand Up @@ -1046,8 +1046,8 @@ to its `.init` value after it is moved into target, otherwise it is
left unchanged.

Preconditions:
If source has internal pointers that point to itself, it cannot be moved, and
will trigger an assertion failure.
If source has internal pointers that point to itself and doesn't define
opPostMove, it cannot be moved, and will trigger an assertion failure.

Params:
source = Data to copy.
Expand Down Expand Up @@ -1198,6 +1198,24 @@ pure nothrow @safe @nogc unittest
assert(s2.a == 2);
}

/// `opPostMove` will be called if defined:
pure nothrow @safe @nogc unittest
{
struct S
{
int a;
void opPostMove(const ref S old)
{
assert(a == old.a);
a++;
}
}
S s1;
s1.a = 41;
S s2 = move(s1);
assert(s2.a == 42);
}

private void trustedMoveImpl(T)(ref T source, ref T target) @trusted
{
moveImpl(source, target);
Expand Down Expand Up @@ -1379,12 +1397,14 @@ void moveEmplace(T)(ref T source, ref T target) @system
import core.stdc.string : memcpy, memset;
import std.traits : hasAliasing, hasElaborateAssign,
hasElaborateCopyConstructor, hasElaborateDestructor,
hasElaborateMove,
isAssignable, isStaticArray;

static if (!is(T == class) && hasAliasing!T) if (!__ctfe)
{
import std.exception : doesPointTo;
assert(!doesPointTo(source, source), "Cannot move object with internal pointer.");
assert(!(doesPointTo(source, source) && !hasElaborateMove!T),
"Cannot move object with internal pointer unless `opPostMove` is defined.");
}

static if (is(T == struct))
Expand All @@ -1396,6 +1416,9 @@ void moveEmplace(T)(ref T source, ref T target) @system
else
target = source;

static if (hasElaborateMove!T)
__move_post_blt(target, source);

// If the source defines a destructor or a postblit hook, we must obliterate the
// object in order to avoid double freeing and undue aliasing
static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
Expand Down
42 changes: 42 additions & 0 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
* $(LREF hasElaborateAssign)
* $(LREF hasElaborateCopyConstructor)
* $(LREF hasElaborateDestructor)
* $(LREF hasElaborateMove)
* $(LREF hasIndirections)
* $(LREF hasMember)
* $(LREF hasStaticMember)
Expand Down Expand Up @@ -3791,6 +3792,47 @@ template hasElaborateDestructor(S)
static assert( hasElaborateDestructor!S7);
}

/**
True if `S` or any type embedded directly in the representation of `S`
defines elaborate move semantics. Elaborate move semantics are
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I like 'elaborate move semantics', but the DIP doesn't propose any terminology. Ideas?

introduced by defining `opPostMove(ref typeof(this))` for a `struct`.

Classes and unions never have elaborate move semantics.
*/
template hasElaborateMove(S)
{
import core.internal.traits : hasElabMove = hasElaborateMove;
alias hasElaborateMove = hasElabMove!(S);
}

///
@safe unittest
{
static assert(!hasElaborateMove!int);

static struct S1 { }
static struct S2 { void opPostMove(ref S2) {} }
static struct S3 { void opPostMove(inout ref S3) inout {} }
static struct S4 { void opPostMove(const ref S4) {} }
static struct S5 { void opPostMove(S5) {} }
static struct S6 { void opPostMove(int) {} }
static struct S7 { S3[1] field; }
static struct S8 { S3[] field; }
static struct S9 { S3[0] field; }
static struct S10 { @disable this(); S3 field; }
static assert(!hasElaborateMove!S1);
static assert( hasElaborateMove!S2);
static assert( hasElaborateMove!S3);
static assert( hasElaborateMove!(immutable S3));
static assert( hasElaborateMove!S4);
static assert(!hasElaborateMove!S5);
static assert(!hasElaborateMove!S6);
static assert( hasElaborateMove!S7);
static assert(!hasElaborateMove!S8);
static assert(!hasElaborateMove!S9);
static assert( hasElaborateMove!S10);
}

package alias Identity(alias A) = A;

/**
Expand Down