From 4be9b5f59fd1cb157c21eb5ed4dc291432c62b4a Mon Sep 17 00:00:00 2001 From: Les De Ridder Date: Mon, 10 Jun 2019 21:07:29 +0200 Subject: [PATCH] Implement Phobos side of DIP1014 --- std/algorithm/mutation.d | 29 ++++++++++++++++++++++++--- std/traits.d | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index f0fe03f2668..dc7c9c8318d 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -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. @@ -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); @@ -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)) @@ -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) diff --git a/std/traits.d b/std/traits.d index 22bd6b8fe29..34f8e45fcef 100644 --- a/std/traits.d +++ b/std/traits.d @@ -42,6 +42,7 @@ * $(LREF hasElaborateAssign) * $(LREF hasElaborateCopyConstructor) * $(LREF hasElaborateDestructor) + * $(LREF hasElaborateMove) * $(LREF hasIndirections) * $(LREF hasMember) * $(LREF hasStaticMember) @@ -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 + 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; /**