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 In D1 ModuleInfo is a class. In D2 it is a struct. ModuleInfoPtr aliases 109 to matching reference type for each of those. 110 111 *******************************************************************************/ 112 113 alias ModuleInfo* ModuleInfoPtr; 114 115 /******************************************************************************* 116 117 In D2 variables are thread-local by default. In many cases this is exactly 118 what you need but sometimes true globals are necessary - primarily related 119 to thread and related tool implementation. 120 121 This small mixin helper prepends __gshared to input declaration when 122 compiled in D2 mode. 123 124 *******************************************************************************/ 125 126 istring global(istring decl) 127 { 128 return "__gshared " ~ decl ~ ";"; 129 } 130 131 unittest 132 { 133 mixin(global("int x = 42")); 134 assert(x == 42); 135 } 136 137 /******************************************************************************* 138 139 In D2 .min was renamed to .min_normal for floating point types to 140 make its meaning more obvious. This is a trivial template wrapper that 141 unifies the naming. 142 143 Params: 144 T = any floating point type 145 146 Return: 147 minimal normalized value for that type 148 149 *******************************************************************************/ 150 151 template min_normal(T : real) 152 { 153 static immutable min_normal = T.min_normal; 154 } 155 156 unittest 157 { 158 static assert (min_normal!(double) == double.min_normal); 159 } 160 161 /******************************************************************************* 162 163 Mixin helper to generate proper opCmp declaration. It differs between D1 164 and D2 runtime and not matching exact signature will result in weird 165 segmentation faults from inside the runtime. 166 167 Params: 168 func_body = code of opCmp as string. Must refer to argument as `rhs` 169 170 Returns: 171 full declaration/definition of opCmp that matches current compiler 172 173 *******************************************************************************/ 174 175 istring genOpCmp(istring func_body) 176 { 177 istring result; 178 179 // We need to know if it's a class or not. If it is, one might want to 180 // compare against literals, so we cannot take it by `const ref`. 181 182 result ~= "static if (is(typeof(this) == class))\n{\n"; 183 result ~= "override int opCmp(Object rhs)\n"; 184 result ~= func_body; 185 result ~= "\n}\nelse\n{\n"; 186 result ~= "int opCmp(const typeof(this) rhs) const\n"; 187 result ~= func_body; 188 result ~= "\n}\n"; 189 190 return result; 191 } 192 193 unittest 194 { 195 struct S 196 { 197 int x; 198 199 mixin (genOpCmp(" 200 { 201 if (this.x >= rhs.x) 202 return this.x > rhs.x; 203 return -1; 204 } 205 ")); 206 207 equals_t opEquals (S rhs) 208 { 209 return (&this).opCmp(rhs) == 0; 210 } 211 } 212 213 class C 214 { 215 private int x; 216 217 this (int a) 218 { 219 this.x = a; 220 } 221 222 mixin (genOpCmp( 223 `{ 224 auto o = cast(typeof(this)) rhs; 225 if (o is null) 226 return -1; 227 if (this.x >= o.x) 228 return this.x > o.x; 229 return -1; 230 }`)); 231 } 232 233 assert (S(1) < S(2)); 234 assert (S(2) > S(1)); 235 assert (S(2) >= S(2)); 236 assert (S(2) <= S(2)); 237 assert (new C(1) < new C(2)); 238 assert (new C(2) > new C(1)); 239 assert (new C(2) >= new C(2)); 240 assert (new C(2) <= new C(2)); 241 } 242 243 /******************************************************************************* 244 245 Mixin helper to generate proper opEquals declaration. It differs between D1 246 and D2 runtime and not matching exact signature will result in the default 247 version, defined by the compiler, to be silently called instead. 248 249 Params: 250 func_body = code of opEquals as string. Must refer to argument as `rhs` 251 252 Returns: 253 full declaration/definition of opEquals that matches current compiler 254 255 *******************************************************************************/ 256 257 public istring genOpEquals(istring func_body) 258 { 259 istring result; 260 261 // We need to know if it's a class or not. If it is, one might want to 262 // compare against literals, so we cannot take it by `const ref`. 263 264 result ~= "static if (is(typeof(this) == class))\n{\n"; 265 result ~= "override equals_t opEquals(Object rhs)\n"; 266 result ~= func_body; 267 result ~= "\n}\nelse\n{\n"; 268 result ~= "bool opEquals(const typeof(this) rhs) const\n"; 269 result ~= func_body; 270 result ~= "\n}\n"; 271 272 return result; 273 } 274 275 unittest 276 { 277 struct S 278 { 279 int x; 280 int y; 281 282 // Use a crazy definition, as the default one would pass those tests :) 283 mixin (genOpEquals(" 284 { 285 return this.x == rhs.y && this.y == rhs.x; 286 } 287 ")); 288 } 289 290 class C 291 { 292 private int x; 293 294 this (int a) 295 { 296 this.x = a; 297 } 298 299 mixin (genOpEquals( 300 `{ 301 auto o = cast(typeof(this)) rhs; 302 if (o is null) return false; 303 return (this.x == o.x); 304 }`)); 305 } 306 307 assert (S(2, 1) == S(1, 2)); 308 assert (new C(2) == new C(2)); 309 310 assert (S(1, 2) != S(1, 2)); 311 assert (S(2, 1) != S(2, 1)); 312 assert (!(S(1, 2) == S(1, 2))); 313 assert (!(S(2, 1) == S(2, 1))); 314 315 C nil; 316 317 assert (new C(1) != new C(2)); 318 assert (new C(2) != new C(1)); 319 assert (!(new C(1) == new C(2))); 320 assert (!(new C(2) == new C(1))); 321 assert (new C(1) != nil); 322 assert (!(new C(1) == nil)); 323 324 // D1 runtime dereference null if it's LHS... 325 //assert (nil != new C(2)); 326 //assert (!(nil == new C(2))); 327 } 328 329 /******************************************************************************* 330 331 In D1, typeof(this) is always a reference type to the aggregate, while in 332 D2 it's the actual type of the aggregate. It doesn't change anything for 333 classes which are reference types, but for struct and unions, it yields 334 a pointer instead of the actual type. 335 d1tod2fix does the conversion automatically for `structs`, but there are 336 places where manual intervention is needed (e.g. `mixin template`s). 337 338 *******************************************************************************/ 339 340 public template TypeofThis() 341 { 342 alias typeof(this) This; 343 } 344 345 version (UnitTest) 346 { 347 private struct FooClass 348 { 349 mixin TypeofThis; 350 } 351 private struct FooStruct 352 { 353 mixin TypeofThis; 354 } 355 356 private union FooUnion 357 { 358 mixin TypeofThis; 359 } 360 } 361 362 unittest 363 { 364 static assert (is(FooClass.This == FooClass)); 365 static assert (is(FooStruct.This == FooStruct)); 366 static assert (is(FooUnion.This == FooUnion)); 367 } 368 369 /******************************************************************************* 370 371 Helper which provides stub implementation of GC.usage if it is not present 372 upstream in order to keep ocean compiling and passing tests. 373 374 *******************************************************************************/ 375 376 static import core.memory; 377 378 static if (is(typeof(core.memory.GC.stats))) 379 { 380 void gc_usage ( out size_t used, out size_t free ) 381 { 382 auto stats = core.memory.GC.stats(); 383 used = stats.usedSize; 384 free = stats.freeSize; 385 } 386 } 387 else static if (is(typeof(core.memory.GC.usage))) 388 { 389 alias core.memory.GC.usage gc_usage; 390 } 391 else 392 { 393 void gc_usage ( out size_t used, out size_t free ) 394 { 395 // no-op 396 } 397 } 398 399 /******************************************************************************* 400 401 Utility intended to help with situations when generic function had to return 402 its templated argument which could turn out to be a static array. In D1 that 403 would require slicing such argument as returning static array types is not 404 allowed. In D2, however, static arrays are value types and such slicing is 405 neither necessary nor memory-safe. 406 407 Examples: 408 409 --- 410 SliceIfD1StaticArray!(T) foo ( T ) ( T input ) 411 { 412 return input; 413 } 414 415 foo(42); 416 foo("abcd"); // wouldn't work if `foo` tried to return just T 417 --- 418 419 Params: 420 T = any type 421 422 Returns: 423 If T is a static array, evaluates to matching dynamic array. Othwerwise 424 evaluates to just T. 425 426 *******************************************************************************/ 427 428 public template SliceIfD1StaticArray ( T ) 429 { 430 alias T SliceIfD1StaticArray; 431 } 432 433 unittest 434 { 435 static assert (is(SliceIfD1StaticArray!(int[4]) == int[4])); 436 static assert (is(SliceIfD1StaticArray!(int[]) == int[])); 437 static assert (is(SliceIfD1StaticArray!(double) == double)); 438 }