1 /*******************************************************************************
2 
3     This modules contains utilities and aliases that are used during D1->D2
4     transition process. Idea is to define single module that contains wrapper
5     aliases / structures and switch all code to use it. Once actual porting
6     time comes it will be enough to simply change version in this module.
7 
8     version(D2) can't be used because D2 code can't be even parsed by D1
9     compiler, resorting to commenting out because of that.
10 
11     Copyright:
12         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
13         All rights reserved.
14 
15     License:
16         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
17         Alternatively, this file may be distributed under the terms of the Tango
18         3-Clause BSD License (see LICENSE_BSD.txt for details).
19 
20 *******************************************************************************/
21 
22 module ocean.transition;
23 
24 import ocean.meta.traits.Basic;
25 import ocean.meta.types.Arrays;
26 
27 public import ocean.core.TypeConvert : assumeUnique;
28 public import ocean.meta.types.Qualifiers;
29 public import ocean.meta.types.Typedef;
30 
31 /*******************************************************************************
32 
33     Checks (non-transitively) if type is mutable
34 
35     Params:
36         T = any plain type
37 
38 *******************************************************************************/
39 
40 template isMutable( T )
41 {
42     enum isMutable = !is(T == const) && !is(T == immutable);
43 }
44 
45 unittest
46 {
47     static assert (!isMutable!(typeof("aaa"[0])));
48 }
49 
50 /*******************************************************************************
51 
52     Helper to smooth transition between D1 and D2 runtime behaviours regarding
53     array stomping. In D2 appending to array slice after length has been changed
54     results in allocating new array to prevent overwriting old data.
55 
56     We use and actually rely on that behaviour for buffer re-usage.
57     `assumeSafeAppend` from object.d enables stomping back but adding using this
58     no-op wrapper in D1 code will save time on trying to find those extremely
59     subtle issues upon actual transition.
60 
61     All places that reset length to 0 will need to call this helper.
62 
63     Params:
64         array = array slice that is going to be overwritten
65 
66 *******************************************************************************/
67 
68 void enableStomping(T)(ref T[] array)
69 {
70     static assert (
71         is(T == Unqual!(T)),
72         "Must not call `enableStomping` on const/immutable array"
73     );
74 
75     assumeSafeAppend(array);
76 }
77 
78 /*******************************************************************************
79 
80     Helper template that can be used instead of octal literals. In some cases
81     preserving octal notation is really important for readability and those
82     can't be simply replace with decimal/hex ones.
83 
84     Params:
85         literal = octal number literal as string
86 
87 *******************************************************************************/
88 
89 static import ocean.text.convert.Integer_tango;
90 
91 template Octal(istring literal)
92 {
93     static immutable Octal = ocean.text.convert.Integer_tango.parse(literal, 8);
94 }
95 
96 unittest
97 {
98     static assert (Octal!("00") == 0);
99     static assert (Octal!("12") == 10);
100     static assert (Octal!("1") == 1);
101     static assert (Octal!("0001") == 1);
102     static assert (Octal!("0010") == 8);
103     static assert (Octal!("666") == (6 + 8*6 + 8*8*6));
104 }
105 
106 /*******************************************************************************
107 
108     Mixin helper to generate proper opCmp declaration. It differs between D1
109     and D2 runtime and not matching exact signature will result in weird
110     segmentation faults from inside the runtime.
111 
112     Params:
113         func_body = code of opCmp as string. Must refer to argument as `rhs`
114 
115     Returns:
116         full declaration/definition of opCmp that matches current compiler
117 
118 *******************************************************************************/
119 
120 istring genOpCmp(istring func_body)
121 {
122     istring result;
123 
124     // We need to know if it's a class or not. If it is, one might want to
125     // compare against literals, so we cannot take it by `const ref`.
126 
127     result ~= "static if (is(typeof(this) == class))\n{\n";
128     result ~= "override int opCmp(Object rhs)\n";
129     result ~= func_body;
130     result ~= "\n}\nelse\n{\n";
131     result ~= "int opCmp(const typeof(this) rhs) const\n";
132     result ~= func_body;
133     result ~= "\n}\n";
134 
135     return result;
136 }
137 
138 unittest
139 {
140     struct S
141     {
142         int x;
143 
144         mixin (genOpCmp("
145         {
146             if (this.x >= rhs.x)
147                 return this.x > rhs.x;
148             return -1;
149         }
150         "));
151 
152         equals_t opEquals (S rhs)
153         {
154             return this.opCmp(rhs) == 0;
155         }
156     }
157 
158     class C
159     {
160         private int x;
161 
162         this (int a)
163         {
164             this.x = a;
165         }
166 
167         mixin (genOpCmp(
168         `{
169             auto o = cast(typeof(this)) rhs;
170             if (o is null)
171                 return -1;
172             if (this.x >= o.x)
173                 return this.x > o.x;
174             return -1;
175         }`));
176     }
177 
178     assert (S(1) < S(2));
179     assert (S(2) > S(1));
180     assert (S(2) >= S(2));
181     assert (S(2) <= S(2));
182     assert (new C(1) < new C(2));
183     assert (new C(2) > new C(1));
184     assert (new C(2) >= new C(2));
185     assert (new C(2) <= new C(2));
186 }
187 
188 /*******************************************************************************
189 
190     Mixin helper to generate proper opEquals declaration. It differs between D1
191     and D2 runtime and not matching exact signature will result in the default
192     version, defined by the compiler, to be silently called instead.
193 
194     Params:
195         func_body = code of opEquals as string. Must refer to argument as `rhs`
196 
197     Returns:
198         full declaration/definition of opEquals that matches current compiler
199 
200 *******************************************************************************/
201 
202 public istring genOpEquals(istring func_body)
203 {
204     istring result;
205 
206     // We need to know if it's a class or not. If it is, one might want to
207     // compare against literals, so we cannot take it by `const ref`.
208 
209     result ~= "static if (is(typeof(this) == class))\n{\n";
210     result ~= "override equals_t opEquals(Object rhs)\n";
211     result ~= func_body;
212     result ~= "\n}\nelse\n{\n";
213     result ~= "bool opEquals(const typeof(this) rhs) const\n";
214     result ~= func_body;
215     result ~= "\n}\n";
216 
217     return result;
218 }
219 
220 unittest
221 {
222     struct S
223     {
224         int x;
225         int y;
226 
227         // Use a crazy definition, as the default one would pass those tests :)
228         mixin (genOpEquals("
229         {
230             return this.x == rhs.y && this.y == rhs.x;
231         }
232         "));
233     }
234 
235     class C
236     {
237         private int x;
238 
239         this (int a)
240         {
241             this.x = a;
242         }
243 
244         mixin (genOpEquals(
245         `{
246             auto o = cast(typeof(this)) rhs;
247             if (o is null) return false;
248             return (this.x == o.x);
249         }`));
250     }
251 
252     assert (S(2, 1) == S(1, 2));
253     assert (new C(2) == new C(2));
254 
255     assert (S(1, 2) != S(1, 2));
256     assert (S(2, 1) != S(2, 1));
257     assert (!(S(1, 2) == S(1, 2)));
258     assert (!(S(2, 1) == S(2, 1)));
259 
260     C nil;
261 
262     assert (new C(1) != new C(2));
263     assert (new C(2) != new C(1));
264     assert (!(new C(1) == new C(2)));
265     assert (!(new C(2) == new C(1)));
266     assert (new C(1) != nil);
267     assert (!(new C(1) == nil));
268 
269     // D1 runtime dereference null if it's LHS...
270     //assert (nil != new C(2));
271     //assert (!(nil == new C(2)));
272 }
273 
274 /*******************************************************************************
275 
276     In D1, typeof(this) is always a reference type to the aggregate, while in
277     D2 it's the actual type of the aggregate. It doesn't change anything for
278     classes which are reference types, but for struct and unions, it yields
279     a pointer instead of the actual type.
280     d1tod2fix does the conversion automatically for `structs`, but there are
281     places where manual intervention is needed (e.g. `mixin template`s).
282 
283 *******************************************************************************/
284 
285 public template TypeofThis()
286 {
287     alias typeof(this) This;
288 }
289 
290 version (unittest)
291 {
292     private struct FooClass
293     {
294         mixin TypeofThis;
295     }
296     private struct FooStruct
297     {
298         mixin TypeofThis;
299     }
300 
301     private union FooUnion
302     {
303         mixin TypeofThis;
304     }
305 }
306 
307 unittest
308 {
309     static assert (is(FooClass.This == FooClass));
310     static assert (is(FooStruct.This == FooStruct));
311     static assert (is(FooUnion.This == FooUnion));
312 }
313 
314 /*******************************************************************************
315 
316     Helper which provides stub implementation of GC.usage if it is not present
317     upstream in order to keep ocean compiling and passing tests.
318 
319 *******************************************************************************/
320 
321 static import core.memory;
322 
323 static if (is(typeof(core.memory.GC.stats)))
324 {
325     void gc_usage ( out size_t used, out size_t free )
326     {
327         auto stats = core.memory.GC.stats();
328         used = stats.usedSize;
329         free = stats.freeSize;
330     }
331 }
332 else static if (is(typeof(core.memory.GC.usage)))
333 {
334     alias core.memory.GC.usage gc_usage;
335 }
336 else
337 {
338     void gc_usage ( out size_t used, out size_t free )
339     {
340         // no-op
341     }
342 }
343 
344 /*******************************************************************************
345 
346     Utility intended to help with situations when generic function had to return
347     its templated argument which could turn out to be a static array. In D1 that
348     would require slicing such argument as returning static array types is not
349     allowed. In D2, however, static arrays are value types and such slicing is
350     neither necessary nor memory-safe.
351 
352     Examples:
353 
354     ---
355     SliceIfD1StaticArray!(T) foo ( T ) ( T input )
356     {
357         return input;
358     }
359 
360     foo(42);
361     foo("abcd"); // wouldn't work if `foo` tried to return just T
362     ---
363 
364     Params:
365         T = any type
366 
367     Returns:
368         If T is a static array, evaluates to matching dynamic array. Othwerwise
369         evaluates to just T.
370 
371 *******************************************************************************/
372 
373 public template SliceIfD1StaticArray ( T )
374 {
375     alias T SliceIfD1StaticArray;
376 }
377 
378 unittest
379 {
380     static assert (is(SliceIfD1StaticArray!(int[4]) == int[4]));
381     static assert (is(SliceIfD1StaticArray!(int[]) == int[]));
382     static assert (is(SliceIfD1StaticArray!(double) == double));
383 }