xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/core/internal/array/construction.d (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1 /**
2  This module contains compiler support for constructing dynamic arrays
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/_array/_construction.d)
9 */
10 module core.internal.array.construction;
11 
12 import core.internal.traits : Unqual;
13 
14 /**
15  * Does array initialization (not assignment) from another array of the same element type.
16  * Params:
17  *  to = what array to initialize
18  *  from = what data the array should be initialized with
19  *  makeWeaklyPure = unused; its purpose is to prevent the function from becoming
20  *      strongly pure and risk being optimised out
21  * Returns:
22  *  The created and initialized array `to`
23  * Bugs:
24  *  This function template was ported from a much older runtime hook that bypassed safety,
25  *  purity, and throwabilty checks. To prevent breaking existing code, this function template
26  *  is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
27  *
28  *  The third parameter is never used, but is necessary in order for the
29  *  function be treated as weakly pure, instead of strongly pure.
30  *  This is needed because constructions such as the one below can be ignored by
31  *  the compiler if `_d_arrayctor` is believed to be pure, because purity would
32  *  mean the call to `_d_arrayctor` has no effects (no side effects and the
33  *  return value is ignored), despite it actually modifying the contents of `a`.
34  *      const S[2] b;
35  *      const S[2] a = b;  // this would get lowered to _d_arrayctor(a, b)
36  */
37 Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted
38 {
39     pragma(inline, false);
40     import core.internal.traits : hasElaborateCopyConstructor;
41     import core.lifetime : copyEmplace;
42     import core.stdc.string : memcpy;
43     import core.stdc.stdint : uintptr_t;
44     debug(PRINTF) import core.stdc.stdio : printf;
45 
46     debug(PRINTF) printf("_d_arrayctor(from = %p,%d) size = %d\n", from.ptr, from.length, T.sizeof);
47 
48     void[] vFrom = (cast(void*) from.ptr)[0..from.length];
49     void[] vTo = (cast(void*) to.ptr)[0..to.length];
50 
51     // Force `enforceRawArraysConformable` to remain weakly `pure`
enforceRawArraysConformable(const char[]action,const size_t elementSize,const void[]a1,const void[]a2)52     void enforceRawArraysConformable(const char[] action, const size_t elementSize,
53         const void[] a1, const void[] a2) @trusted
54     {
55         import core.internal.util.array : enforceRawArraysConformableNogc;
56 
57         alias Type = void function(const char[] action, const size_t elementSize,
58             const void[] a1, const void[] a2, in bool allowOverlap = false) @nogc pure nothrow;
59         (cast(Type)&enforceRawArraysConformableNogc)(action, elementSize, a1, a2, false);
60     }
61 
62     enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo);
63 
64     static if (hasElaborateCopyConstructor!T)
65     {
66         size_t i;
67         try
68         {
69             for (i = 0; i < to.length; i++)
70                 copyEmplace(from[i], to[i]);
71         }
catch(Exception o)72         catch (Exception o)
73         {
74             /* Destroy, in reverse order, what we've constructed so far
75             */
76             while (i--)
77             {
78                 auto elem = cast(Unqual!T*) &to[i];
79                 destroy(*elem);
80             }
81 
82             throw o;
83         }
84     }
85     else
86     {
87         // blit all elements at once
88         memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
89     }
90 
91     return to;
92 }
93 
94 // postblit
95 @safe unittest
96 {
97     int counter;
98     struct S
99     {
100         int val;
thisS101         this(this) { counter++; }
102     }
103 
104     S[4] arr1;
105     S[4] arr2 = [S(0), S(1), S(2), S(3)];
106     _d_arrayctor(arr1[], arr2[]);
107 
108     assert(counter == 4);
109     assert(arr1 == arr2);
110 }
111 
112 // copy constructor
113 @safe unittest
114 {
115     int counter;
116     struct S
117     {
118         int val;
thisS119         this(int val) { this.val = val; }
thisS120         this(const scope ref S rhs)
121         {
122             val = rhs.val;
123             counter++;
124         }
125     }
126 
127     S[4] arr1;
128     S[4] arr2 = [S(0), S(1), S(2), S(3)];
129     _d_arrayctor(arr1[], arr2[]);
130 
131     assert(counter == 4);
132     assert(arr1 == arr2);
133 }
134 
135 @safe nothrow unittest
136 {
137     // Test that throwing works
138     int counter;
139     bool didThrow;
140 
141     struct Throw
142     {
143         int val;
thisThrow144         this(this)
145         {
146             counter++;
147             if (counter == 2)
148                 throw new Exception("");
149         }
150     }
151     try
152     {
153         Throw[4] a;
154         Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
155         _d_arrayctor(a[], b[]);
156     }
catch(Exception)157     catch (Exception)
158     {
159         didThrow = true;
160     }
161     assert(didThrow);
162     assert(counter == 2);
163 
164 
165     // Test that `nothrow` works
166     didThrow = false;
167     counter = 0;
168     struct NoThrow
169     {
170         int val;
thisNoThrow171         this(this)
172         {
173             counter++;
174         }
175     }
176     try
177     {
178         NoThrow[4] a;
179         NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)];
180         _d_arrayctor(a[], b[]);
181     }
catch(Exception)182     catch (Exception)
183     {
184         didThrow = false;
185     }
186     assert(!didThrow);
187     assert(counter == 4);
188 }
189 
190 /**
191  * Do construction of an array.
192  *      ti[count] p = value;
193  * Params:
194  *  p = what array to initialize
195  *  value = what data to construct the array with
196  * Bugs:
197  *  This function template was ported from a much older runtime hook that bypassed safety,
198  *  purity, and throwabilty checks. To prevent breaking existing code, this function template
199  *  is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
200  */
201 void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted
202 {
203     pragma(inline, false);
204     import core.lifetime : copyEmplace;
205 
206     size_t i;
207     try
208     {
209         for (i = 0; i < p.length; i++)
210             copyEmplace(value, p[i]);
211     }
catch(Exception o)212     catch (Exception o)
213     {
214         // Destroy, in reverse order, what we've constructed so far
215         while (i--)
216         {
217             auto elem = cast(Unqual!T*)&p[i];
218             destroy(*elem);
219         }
220 
221         throw o;
222     }
223 }
224 
225 // postblit
226 @safe unittest
227 {
228     int counter;
229     struct S
230     {
231         int val;
thisS232         this(this)
233         {
234             counter++;
235         }
236     }
237 
238     S[4] arr;
239     S s = S(1234);
240     _d_arraysetctor(arr[], s);
241     assert(counter == arr.length);
242     assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
243 }
244 
245 // copy constructor
246 @safe unittest
247 {
248     int counter;
249     struct S
250     {
251         int val;
thisS252         this(int val) { this.val = val; }
thisS253         this(const scope ref S rhs)
254         {
255             val = rhs.val;
256             counter++;
257         }
258     }
259 
260     S[4] arr;
261     S s = S(1234);
262     _d_arraysetctor(arr[], s);
263     assert(counter == arr.length);
264     assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
265 }
266 
267 @safe nothrow unittest
268 {
269     // Test that throwing works
270     int counter;
271     bool didThrow;
272     struct Throw
273     {
274         int val;
thisThrow275         this(this)
276         {
277             counter++;
278             if (counter == 2)
279                 throw new Exception("Oh no.");
280         }
281     }
282     try
283     {
284         Throw[4] a;
285         Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
286         _d_arrayctor(a[], b[]);
287     }
catch(Exception)288     catch (Exception)
289     {
290         didThrow = true;
291     }
292     assert(didThrow);
293     assert(counter == 2);
294 
295 
296     // Test that `nothrow` works
297     didThrow = false;
298     counter = 0;
299     struct NoThrow
300     {
301         int val;
thisNoThrow302         this(this)
303         {
304             counter++;
305         }
306     }
307     try
308     {
309         NoThrow[4] a;
310         NoThrow b = NoThrow(1);
311         _d_arraysetctor(a[], b);
312         foreach (ref e; a)
313             assert(e == NoThrow(1));
314     }
catch(Exception)315     catch (Exception)
316     {
317         didThrow = false;
318     }
319     assert(!didThrow);
320     assert(counter == 4);
321 }
322