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 }