1 /**
2  This module contains the implementation of move semantics of DIP 1014
3 
4   Copyright: Copyright Digital Mars 2000 - 2019.
5   License: Distributed under the
6        $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7      (See accompanying file LICENSE)
8   Source: $(DRUNTIMESRC core/_internal/_moving.d)
9 */
10 module core.internal.moving;
11 
12 /**
13 Recursively calls the `opPostMove` callbacks of a struct and its members if
14 they're defined.
15 
16 When moving a struct instance, the compiler emits a call to this function
17 after blitting the instance and before releasing the original instance's
18 memory.
19 
20 Params:
21      newLocation = reference to struct instance being moved into
22      oldLocation = reference to the original instance
23 
24 Note:
25      This function is tentatively defined as `nothrow` to prevent
26      `opPostMove` from being defined without `nothrow`, which would allow
27      for possibly confusing changes in program flow.
28 */
29 void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow
30     if (is(S == struct))
31 {
32     import core.internal.traits : hasElaborateMove;
33     static foreach (i, M; typeof(S.tupleof))
34     {
35         static if (hasElaborateMove!M)
36         {
37             __move_post_blt(newLocation.tupleof[i], oldLocation.tupleof[i]);
38         }
39     }
40 
41     static if (__traits(hasMember, S, "opPostMove"))
42     {
43         import core.internal.traits : lvalueOf, rvalueOf;
44         static assert( is(typeof(S.init.opPostMove(lvalueOf!S))) &&
45                       !is(typeof(S.init.opPostMove(rvalueOf!S))),
46                 "`" ~ S.stringof ~ ".opPostMove` must take exactly one argument of type `" ~ S.stringof ~ "` by reference");
47 
48         newLocation.opPostMove(oldLocation);
49     }
50 }
51 
52 void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow
53     if (__traits(isStaticArray, S))
54 {
55     import core.internal.traits : hasElaborateMove;
56     static if (S.length && hasElaborateMove!(typeof(newLocation[0])))
57     {
58         foreach (i; 0 .. S.length)
59             __move_post_blt(newLocation[i], oldLocation[i]);
60     }
61 }
62 
63 @safe nothrow unittest
64 {
65     struct A
66     {
67         bool movedInto;
opPostMoveA68         void opPostMove(const ref A oldLocation)
69         {
70             movedInto = true;
71         }
72     }
73     A src, dest;
74     __move_post_blt(dest, src);
75     assert(dest.movedInto);
76 }
77 
78 @safe nothrow unittest
79 {
80     struct A
81     {
82         bool movedInto;
opPostMoveA83         void opPostMove(const ref A oldLocation)
84         {
85             movedInto = true;
86         }
87     }
88     struct B
89     {
90         A a;
91 
92         bool movedInto;
opPostMove(const ref B oldLocation)93         void opPostMove(const ref B oldLocation)
94         {
95             movedInto = true;
96         }
97     }
98     B src, dest;
99     __move_post_blt(dest, src);
100     assert(dest.movedInto && dest.a.movedInto);
101 }
102 
103 @safe nothrow unittest
104 {
105     static struct DoNotMove
106     {
107         bool movedInto;
opPostMoveDoNotMove108         void opPostMove(const ref DoNotMove oldLocation)
109         {
110             movedInto = true;
111         }
112     }
113     static DoNotMove doNotMove;
114 
115     struct A
116     {
memberA117         @property ref DoNotMove member()
118         {
119             return doNotMove;
120         }
121     }
122     A src, dest;
123     __move_post_blt(dest, src);
124     assert(!doNotMove.movedInto);
125 }
126 
127 @safe nothrow unittest
128 {
129     static struct A
130     {
131         bool movedInto;
opPostMoveA132         void opPostMove(const ref A oldLocation)
133         {
134             movedInto = true;
135         }
136     }
137     static struct B
138     {
139         A[2] a;
140     }
141     B src, dest;
142     __move_post_blt(dest, src);
143     foreach (ref a; src.a)
144         assert(!a.movedInto);
145     foreach (ref a; dest.a)
146         assert(a.movedInto);
147 }
148