aboutsummaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/core/internal/moving.d
blob: 9c97d2966f7bf18cf620adcc38bd1f96338f3bec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
 This module contains the implementation of move semantics of DIP 1014

  Copyright: Copyright Digital Mars 2000 - 2019.
  License: Distributed under the
       $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
     (See accompanying file LICENSE)
  Source: $(DRUNTIMESRC core/_internal/_moving.d)
*/
module core.internal.moving;

/**
Recursively calls the `opPostMove` callbacks of a struct and its members if
they're defined.

When moving a struct instance, the compiler emits a call to this function
after blitting the instance and before releasing the original instance's
memory.

Params:
     newLocation = reference to struct instance being moved into
     oldLocation = reference to the original instance

Note:
     This function is tentatively defined as `nothrow` to prevent
     `opPostMove` from being defined without `nothrow`, which would allow
     for possibly confusing changes in program flow.
*/
void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow
    if (is(S == struct))
{
    import core.internal.traits : hasElaborateMove;
    static foreach (i, M; typeof(S.tupleof))
    {
        static if (hasElaborateMove!M)
        {
            __move_post_blt(newLocation.tupleof[i], oldLocation.tupleof[i]);
        }
    }

    static if (__traits(hasMember, S, "opPostMove"))
    {
        import core.internal.traits : lvalueOf, rvalueOf;
        static assert( is(typeof(S.init.opPostMove(lvalueOf!S))) &&
                      !is(typeof(S.init.opPostMove(rvalueOf!S))),
                "`" ~ S.stringof ~ ".opPostMove` must take exactly one argument of type `" ~ S.stringof ~ "` by reference");

        newLocation.opPostMove(oldLocation);
    }
}

void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow
    if (__traits(isStaticArray, S))
{
    import core.internal.traits : hasElaborateMove;
    static if (S.length && hasElaborateMove!(typeof(newLocation[0])))
    {
        foreach (i; 0 .. S.length)
            __move_post_blt(newLocation[i], oldLocation[i]);
    }
}

@safe nothrow unittest
{
    struct A
    {
        bool movedInto;
        void opPostMove(const ref A oldLocation)
        {
            movedInto = true;
        }
    }
    A src, dest;
    __move_post_blt(dest, src);
    assert(dest.movedInto);
}

@safe nothrow unittest
{
    struct A
    {
        bool movedInto;
        void opPostMove(const ref A oldLocation)
        {
            movedInto = true;
        }
    }
    struct B
    {
        A a;

        bool movedInto;
        void opPostMove(const ref B oldLocation)
        {
            movedInto = true;
        }
    }
    B src, dest;
    __move_post_blt(dest, src);
    assert(dest.movedInto && dest.a.movedInto);
}

@safe nothrow unittest
{
    static struct DoNotMove
    {
        bool movedInto;
        void opPostMove(const ref DoNotMove oldLocation)
        {
            movedInto = true;
        }
    }
    static DoNotMove doNotMove;

    struct A
    {
        @property ref DoNotMove member()
        {
            return doNotMove;
        }
    }
    A src, dest;
    __move_post_blt(dest, src);
    assert(!doNotMove.movedInto);
}

@safe nothrow unittest
{
    static struct A
    {
        bool movedInto;
        void opPostMove(const ref A oldLocation)
        {
            movedInto = true;
        }
    }
    static struct B
    {
        A[2] a;
    }
    B src, dest;
    __move_post_blt(dest, src);
    foreach (ref a; src.a)
        assert(!a.movedInto);
    foreach (ref a; dest.a)
        assert(a.movedInto);
}