1 /** Various extensions to std.traits. 2 3 Copyright: Per Nordlöw 2022-. 4 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: $(WEB Per Nordlöw) 6 7 See_Also: http://forum.dlang.org/thread/jbyixfbuefvdlttnyclu@forum.dlang.org#post-mailman.2199.1353742037.5162.digitalmars-d-learn:40puremagic.com 8 See_Also: http://forum.dlang.org/post/rjrdgmijsmvvsbpinidz@forum.dlang.org 9 */ 10 module nxt.traits_ex; 11 12 import std.traits: isArray, ParameterTypeTuple, isDynamicArray, isSomeChar, isSomeString, isExpressions, isIntegral, isSigned, isUnsigned, isAssignable, isIterable; 13 import std.meta : allSatisfy; 14 import std.range: ElementType, isForwardRange, isRandomAccessRange, isInputRange, isBidirectionalRange, isOutputRange; 15 16 /** Returns: `true` iff $(D ptr) is handled by D's garbage collector (GC). 17 */ 18 bool isGCPointer(T)(const T* ptr) 19 @trusted nothrow @nogc 20 { 21 import core.memory : GC; 22 return cast(bool)GC.addrOf(ptr); 23 } 24 25 /// 26 @system nothrow unittest { 27 int s; 28 int* sp = &s; 29 assert(!sp.isGCPointer); 30 int* ip = new int; 31 assert(ip.isGCPointer); 32 } 33 34 /** Returns: `true` iff all values `V` are the same. 35 * 36 * See_Also: https://forum.dlang.org/post/lnsreapgttmdeuscsupp@forum.dlang.org 37 */ 38 template allSameIterative(V...) 39 { 40 static if (V.length <= 1) 41 enum allSameIterative = true; 42 else 43 { 44 static foreach (Vi; V[1 .. $]) 45 static if (is(typeof(allSameIterative) == void) && // not yet defined 46 !isSame!(V[0], Vi)) 47 enum allSameIterative = false; 48 static if (is(typeof(allSameIterative) == void)) // if not yet defined 49 enum allSameIterative = true; 50 } 51 } 52 53 /// 54 pure nothrow @safe @nogc unittest { 55 static assert( allSameIterative!()); 56 static assert( allSameIterative!(42)); 57 static assert( allSameIterative!(42, 42)); 58 static assert( allSameIterative!(42, 42, 42)); 59 static assert(!allSameIterative!(42, 43, 42)); 60 61 static assert( allSameIterative!(int)); 62 static assert( allSameIterative!(int, int)); 63 static assert( allSameIterative!(int, int, int)); 64 static assert(!allSameIterative!(int, byte, int)); 65 } 66 67 alias allSame = allSameIterative; // default to iterative variant for now 68 alias isHomogeneousType = allSame; 69 enum isHomogeneousTuple(T) = isHomogeneousType!(T.Types); 70 71 /// 72 pure nothrow @safe @nogc unittest { 73 static assert(isHomogeneousTuple!(Tuple!(int, int, int))); 74 static assert(isHomogeneousTuple!(Tuple!(float, float, float))); 75 static assert(!isHomogeneousTuple!(Tuple!(int, float, double))); 76 } 77 78 enum isHomogeneousTupleOf(T, E) = (isHomogeneousType!(T) && 79 is(T.Types[0] == E)); 80 81 /// 82 pure nothrow @safe @nogc unittest { 83 static assert(isHomogeneousTupleOf!(Tuple!(int, int, int), int)); 84 static assert(isHomogeneousTupleOf!(Tuple!(float, float, float), float)); 85 static assert(!isHomogeneousTupleOf!(Tuple!(float, float, float), int)); 86 } 87 88 /** 89 Returns $(D true) if at least one type in the $(D Tuple T) 90 is not the same as the others. 91 */ 92 enum isHeterogeneous(T) = !isHomogeneousType!T; 93 94 template allSameTypeIterative(V...) 95 /+ TODO: restrict `V` to types only +/ 96 { 97 static if (V.length >= 2) 98 static foreach (Vi; V[1 .. $]) 99 static if (is(typeof(allSameTypeIterative) == void) && // not yet defined 100 !is(V[0] == Vi)) // 10% faster than `!isSame(V[0], Vi)` 101 enum allSameTypeIterative = false; 102 static if (is(typeof(allSameTypeIterative) == void)) // if not yet defined 103 enum allSameTypeIterative = true; 104 } 105 alias allSameType = allSameTypeIterative; 106 107 /// 108 pure nothrow @safe @nogc unittest { 109 static assert( allSameTypeIterative!(int)); 110 static assert( allSameTypeIterative!(int, int)); 111 112 static assert( allSameTypeIterative!(int, int, int)); 113 static assert(!allSameTypeIterative!(int, byte, int)); 114 115 static assert( allSameTypeIterative!(int, int, int, int)); 116 static assert(!allSameTypeIterative!(int, byte, int, byte)); 117 118 static assert(!allSameTypeIterative!(int, const(int))); 119 static assert(!allSameTypeIterative!(byte, const(int))); 120 } 121 122 /** Returns: `true` iff all values `V` are the same. 123 124 Same as NoDuplicates!V.length == 1 125 126 See_Also: https://forum.dlang.org/post/ptnzlhnkuetijhgrgumd@forum.dlang.org 127 See_Also: http://forum.dlang.org/post/iflpslqgrixdjwrlqqvn@forum.dlang.org 128 See_Also: http://forum.dlang.org/post/mheumktihihfsxxxapff@forum.dlang.org 129 */ 130 template allSameRecursive(V...) 131 if (isExpressions!V) 132 { 133 static if (V.length <= 1) 134 enum allSameRecursive = true; 135 else static if (V.length & 1) // odd count 136 enum allSameRecursive = (V[0] == V[$ - 1] && // first equals last 137 V[0 .. $/2] == V[$/2 .. $-1] && // (first half) equals (second half minus last element) 138 allSameRecursive!(V[0 .. $/2])); 139 else // event count 140 enum allSameRecursive = (V[0 .. $/2] == V[$/2 .. $] && // (first half) equals (second half) 141 allSameRecursive!(V[0 .. $/2])); 142 } 143 144 /// 145 pure nothrow @safe @nogc unittest { 146 static assert( allSameRecursive!()); 147 static assert( allSameRecursive!(42)); 148 static assert( allSameRecursive!(42, 42)); 149 static assert( allSameRecursive!(42, 42, 42)); 150 static assert(!allSameRecursive!(42, 43, 42)); 151 } 152 153 template allSameTypeHybrid(V...) 154 /+ TODO: restrict `V` to types only +/ 155 { 156 static if (V.length >= 8) 157 { 158 static if (V.length <= 1) 159 enum allSameTypeHybrid = true; 160 else static if (V.length == 2) 161 enum allSameTypeHybrid = is(V[0] == V[1]); 162 static if (V.length & 1) // odd count 163 enum allSameTypeHybrid = (is(V[0] == V[$ - 1]) && // first equals last 164 is(V[0 .. $/2] == V[$/2 .. $-1]) && // (first half) equals (second half minus last element) 165 allSameTypeHybrid!(V[0 .. $/2])); 166 else // even count 167 enum allSameTypeHybrid = (is(V[0 .. $/2] == V[$/2 .. $]) && // (first half) equals (second half) 168 allSameTypeHybrid!(V[0 .. $/2])); 169 } 170 else 171 enum allSameTypeHybrid = allSameTypeIterative!(V); 172 } 173 174 /// 175 pure nothrow @safe @nogc unittest { 176 static assert(allSameTypeHybrid!()); 177 static assert(allSameTypeHybrid!(int)); 178 static assert(allSameTypeHybrid!(int, int)); 179 static assert(!allSameTypeHybrid!(int, double)); 180 static assert(!allSameTypeHybrid!(int, int, double)); 181 static assert(allSameTypeHybrid!(Tuple!(int, int, int).Types, int)); 182 183 static assert(!allSameTypeHybrid!(int, const(int))); 184 static assert(!allSameTypeHybrid!(byte, const(int))); 185 } 186 187 /** Variant of `allSameTypeRecursive`. 188 */ 189 template allSameTypeRecursive2(V...) 190 if (isExpressions!(V)) 191 { 192 static if (V.length <= 1) 193 enum allSameTypeRecursive2 = true; 194 else 195 enum allSameTypeRecursive2 = (V[0] == V[1] && 196 allSameTypeRecursive2!(V[1..$])); 197 } 198 199 /// 200 pure nothrow @safe @nogc unittest { 201 static assert(allSameTypeRecursive2!()); 202 static assert(allSameTypeRecursive2!(42)); 203 static assert(!allSameTypeRecursive2!(41, 42)); 204 static assert(allSameTypeRecursive2!(42, 42, 42)); 205 } 206 207 /** Returns: `true` iff all types `T` are the same. 208 */ 209 template allSameTypeRecursive(V...) 210 /+ TODO: restrict `V` to types only +/ 211 { 212 static if (V.length <= 1) 213 enum allSameTypeRecursive = true; 214 else static if (V.length & 1) // odd count 215 enum allSameTypeRecursive = (is(V[0] == V[$ - 1]) && // first equals last 216 is(V[0 .. $/2] == V[$/2 .. $-1]) && // (first half) equals (second half minus last element) 217 allSameTypeRecursive!(V[0 .. $/2])); 218 else // even count 219 enum allSameTypeRecursive = (is(V[0 .. $/2] == V[$/2 .. $]) && // (first half) equals (second half) 220 allSameTypeRecursive!(V[0 .. $/2])); 221 } 222 223 /// 224 pure nothrow @safe @nogc unittest { 225 static assert(allSameTypeRecursive!()); 226 static assert(allSameTypeRecursive!(int)); 227 static assert(allSameTypeRecursive!(int, int)); 228 static assert(!allSameTypeRecursive!(int, double)); 229 static assert(!allSameTypeRecursive!(int, int, double)); 230 static assert(allSameTypeRecursive!(Tuple!(int, int, int).Types, int)); 231 232 static assert(!allSameTypeRecursive!(int, const(int))); 233 static assert(!allSameTypeRecursive!(byte, const(int))); 234 } 235 236 /** Returns: `true` iff all types `T` are the same. */ 237 enum allSameType_alternative(T...) = (!T.length || 238 (is(T[0] == T[T.length > 1]) && 239 allSameType1!(T[1 .. $]))); 240 241 242 import std.typecons : isTuple; 243 244 /** 245 Returns $(D true) if all types in the $(D Tuple T) are the same. 246 TODO: Remove when this is merged: https://github.com/D-Programming-Language/phobos/pull/3395 247 See_Also: https://github.com/D-Programming-Language/phobos/pull/1672/files 248 */ 249 template allSameTypesInTuple(T) 250 if (isTuple!T) 251 { 252 alias types = T.Types; 253 static if (types.length > 0) 254 { 255 template isSameTypeAsHead(U) 256 { 257 enum isSameTypeAsHead = is(U == types[0]); 258 } 259 import std.meta : allSatisfy; 260 enum allSameTypesInTuple = allSatisfy!(isSameTypeAsHead, types); 261 } 262 else 263 enum allSameTypesInTuple = true; 264 } 265 266 /// 267 pure nothrow @safe unittest { 268 alias HOTUP = Tuple!(int, int, int); 269 static assert(allSameTypesInTuple!HOTUP); 270 271 const HOTUP hotup = HOTUP(1, 2, 3); 272 static assert(allSameTypesInTuple!(typeof(hotup))); 273 274 alias HETUP = Tuple!(string, bool, float); 275 static assert(!allSameTypesInTuple!(HETUP)); 276 277 const HETUP hetup = HETUP("test", false, 2.345); 278 static assert(!allSameTypesInTuple!(typeof(hetup))); 279 280 alias ZTUP = Tuple!(); 281 static assert(allSameTypesInTuple!ZTUP); 282 283 const ZTUP ztup = ZTUP(); 284 static assert(allSameTypesInTuple!(typeof(ztup))); 285 } 286 287 /** Returns: tuple `tup` as a dynamic array. 288 */ 289 auto asDynamicArray(T)(inout T tup) 290 if (allSameTypeRecursive!(T.Types)) 291 { 292 alias E = T.Types[0]; 293 E[] a = new E[T.length]; 294 a.length = T.length; 295 foreach (const i, e; tup) 296 a[i] = e; 297 return a; 298 } 299 300 /// 301 pure nothrow unittest { 302 import std.typecons: tuple; 303 auto tup = tuple("a", "b", "c", "d"); 304 string[4] arr = ["a", "b", "c", "d"]; 305 assert(tup.asDynamicArray() == arr); 306 } 307 308 /** Is `true` if `R` is iterable over references to its elements. 309 * 310 * Typically used to iterate over ranges with uncopyable elements. 311 * 312 * TODO: Move to Phobos. 313 */ 314 enum bool isRefIterable(T) = is(typeof({ foreach (ref elem; T.init) {} })); 315 316 /// Useful aliases for combinations of range predicates. 317 enum isIterableOf(R, E) = isIterable!R && is(ElementType!R == E); 318 enum isIterableOfSomeString(R) = (isIterable!R && isSomeString!(ElementType!R)); 319 320 /// 321 pure nothrow @safe @nogc unittest { 322 alias E = string; 323 alias I = int; 324 alias R = typeof(["a", "b"]); 325 static assert(isIterableOf!(R, E)); 326 static assert(isIterableOfSomeString!(R)); 327 static assert(!isIterableOf!(R, I)); 328 } 329 330 /// Useful aliases for combinations of range predicates. 331 alias isForwardRangeOf = isForwardRange; 332 alias isInputRangeOf = isInputRange; 333 334 enum isOutputRangeOf(R, E) = isOutputRange!R && is(ElementType!R == E); 335 enum isArrayOf(R, E) = isArray!R && is(ElementType!R == E); 336 enum isArrayOfSomeString(R) = isArray!R && isSomeString!(ElementType!R); 337 338 enum isSourceAssignableTo(R, E) = (isInputRange!R && 339 isAssignable!(E, ElementType!R)); 340 341 /// 342 pure @safe unittest { 343 static assert(isSomeString!(string)); 344 static assert(isSomeString!(const string)); 345 346 static assert(isSomeString!(const(char)[])); 347 static assert(isSomeString!(const char[])); 348 } 349 350 pure nothrow @safe @nogc unittest { 351 alias R = typeof(["a", "b"]); 352 static assert(isArrayOf!(R, string)); 353 static assert(isArrayOfSomeString!(R)); 354 } 355 356 enum isSource(R) = isInputRange!(R); 357 enum isRange(R) = isInputRange!(R); 358 359 enum isSourceOf(R, E) = isInputRangeOf!(R, E); 360 private enum isSink(R) = isOutputRange!(R); 361 private enum isSinkOf(R, E) = isOutputRangeOf!(R, E); 362 363 enum isSourceOfSomeChar(R) = (isSource!R && 364 isSomeChar!(ElementType!R)); 365 alias isSomeLazyString = isSourceOfSomeChar; 366 367 pure nothrow @safe @nogc unittest { 368 import std.meta : AliasSeq; 369 foreach (Ch; AliasSeq!(char, wchar, dchar)) 370 { 371 assert(isSourceOfSomeChar!(Ch[])); 372 assert(isSourceOfSomeChar!(const(Ch)[])); 373 assert(isSourceOfSomeChar!(immutable(Ch)[])); 374 } 375 } 376 377 enum isSourceOfSomeString(R) = (isSource!R && isSomeString!(ElementType!R)); 378 alias isSomeStringSource = isSourceOfSomeString; 379 380 import std.functional: unaryFun, binaryFun; 381 382 /* TODO: Do we need use of unaryFun and binaryFun here? */ 383 alias isEven = unaryFun!(a => (a & 1) == 0); // Limit to Integers? 384 alias isOdd = unaryFun!(a => (a & 1) == 1); // Limit to Integers? 385 alias lessThan = binaryFun!((a, b) => a < b); 386 alias greaterThan = binaryFun!((a, b) => a > b); 387 388 /** Check if `T` has an even length. */ 389 enum hasEvenLength(T...) = !(T.length & 1); 390 pure nothrow @safe @nogc unittest { 391 static assert(!hasEvenLength!(1)); 392 static assert(hasEvenLength!(1, 2)); 393 static assert(!hasEvenLength!(1, 2, 3)); 394 static assert(hasEvenLength!(1, 2, 3, 4)); 395 } 396 397 enum isSignedIntegral(T) = isIntegral!T && isSigned!T; 398 enum isUnsignedIntegral(T) = isIntegral!T && isUnsigned!T; 399 400 enum isString (T) = is(T == string); 401 enum isWString(T) = is(T == wstring); 402 enum isDString(T) = is(T == dstring); 403 404 enum isEnum(T) = is(T == enum); 405 pure nothrow @safe @nogc unittest { 406 interface I {} 407 class A {} 408 class B( T ) {} 409 class C : B!int, I {} 410 struct S {} 411 enum E { X } 412 static assert(!isEnum!A ); 413 static assert(!isEnum!( B!int ) ); 414 static assert(!isEnum!C ); 415 static assert(!isEnum!I ); 416 static assert(isEnum!E ); 417 static assert(!isEnum!int ); 418 static assert(!isEnum!( int* ) ); 419 } 420 421 /* See_Also: http://d.puremagic.com/issues/show_bug.cgi?id=4427 */ 422 enum isStruct(T) = is(T == struct); 423 pure nothrow @safe @nogc unittest { 424 interface I {} 425 class A {} 426 class B( T ) {} 427 class C : B!int, I {} 428 struct S {} 429 static assert(!isStruct!A ); 430 static assert(!isStruct!( B!int ) ); 431 static assert(!isStruct!C ); 432 static assert(!isStruct!I ); 433 static assert(isStruct!S ); 434 static assert(!isStruct!int ); 435 static assert(!isStruct!( int* ) ); 436 } 437 438 enum isClass(T) = is(T == class); 439 pure nothrow @safe @nogc unittest { 440 interface I {} 441 class A {} 442 class B( T ) {} 443 class C : B!int, I {} 444 struct S {} 445 static assert(isClass!A ); 446 static assert(isClass!( B!int ) ); 447 static assert(isClass!C ); 448 static assert(!isClass!I ); 449 static assert(!isClass!S ); 450 static assert(!isClass!int ); 451 static assert(!isClass!( int* ) ); 452 } 453 454 enum isInterface(T) = is(T == interface); 455 pure nothrow @safe @nogc unittest { 456 interface I {} 457 class A {} 458 class B( T ) {} 459 class C : B!int, I {} 460 struct S {} 461 static assert(!isInterface!A ); 462 static assert(!isInterface!( B!int ) ); 463 static assert(!isInterface!C ); 464 static assert(isInterface!I ); 465 static assert(!isInterface!S ); 466 static assert(!isInterface!int ); 467 static assert(!isInterface!( int* ) ); 468 } 469 470 template isType(T) { enum isType = true; } 471 template isType(alias T) { enum isType = false; } 472 473 pure nothrow @safe @nogc unittest { 474 struct S { alias int foo; } 475 static assert(isType!int ); 476 static assert(isType!float ); 477 static assert(isType!string ); 478 //static assert(isType!S ); // Bugzilla 4431 479 static assert(isType!( S.foo ) ); 480 static assert(!isType!4 ); 481 static assert(!isType!"Hello world!" ); 482 } 483 484 enum nameOf(alias a) = a.stringof; 485 /// 486 pure nothrow @safe @nogc unittest { 487 int var; 488 static assert(nameOf!var == var.stringof); 489 } 490 491 /** Is $(D ElementType) of type of $(D a). */ 492 alias ElementTypeOf(alias a) = ElementType!(typeof(a)); 493 /// 494 pure nothrow @safe @nogc unittest { 495 int[] var; 496 static assert(is(ElementTypeOf!var == int)); 497 } 498 499 template Chainable() 500 { 501 import std.range: chain; 502 auto ref opCast(Range)(Range r) 503 { 504 return chain(this, r); 505 } 506 } 507 pure nothrow @safe @nogc unittest { mixin Chainable; } 508 509 /** Returns true if `T` is an instance of the template `S`. 510 See_Also: http://forum.dlang.org/thread/mailman.2901.1316118301.14074.digitalmars-d-learn@puremagic.com#post-zzdpfhsgfdgpszdbgbbt:40forum.dlang.org 511 */ 512 template isA(alias S, T) 513 { 514 enum isA = is(T == S!(_), _); 515 } 516 517 pure nothrow @safe @nogc unittest { 518 import std.range : SortedRange, assumeSorted; 519 const d = [1, 2, 3].s; 520 const x = d[].assumeSorted; 521 static assert(is(typeof(x) == SortedRange!(_), _...)); 522 static assert(isA!(SortedRange, typeof(x))); 523 } 524 525 /** See_Also: http://forum.dlang.org/thread/bug-6384-3@http.d.puremagic.com/issues/ 526 See_Also: http://forum.dlang.org/thread/jrqiiicmtpenzokfxvlz@forum.dlang.org */ 527 enum isOpBinary(T, string op, U) = is(typeof(mixin("T.init" ~ op ~ "U.init"))); 528 529 enum isComparable(T) = is(typeof({ return T.init < T.init; })); //+ TODO: Move to Phobos std.traits +/ 530 enum isEquable (T) = is(typeof({ return T.init == T.init; })); //+ TODO: Move to Phobos std.traits +/ 531 enum isNotEquable(T) = is(typeof({ return T.init != T.init; })); //+ TODO: Move to Phobos std.traits +/ 532 533 pure nothrow @safe @nogc unittest { 534 static assert(isComparable!int); 535 static assert(isComparable!string); 536 static assert(!isComparable!creal); 537 static struct Foo {} 538 static assert(!isComparable!Foo); 539 static struct Bar { bool opCmp(Bar) { return true; } } 540 static assert(isComparable!Bar); 541 } 542 543 /+ TODO: variadic +/ 544 enum areComparable(T, U) = is(typeof({ return T.init < U.init; })); //+ TODO: Move to Phobos std.traits +/ 545 enum areEquable (T, U) = is(typeof({ return T.init == U.init; })); //+ TODO: Move to Phobos std.traits +/ 546 enum areNotEquable(T, U) = is(typeof({ return T.init != U.init; })); //+ TODO: Move to Phobos std.traits +/ 547 548 pure nothrow @safe @nogc unittest { 549 static assert(areComparable!(int, float)); 550 static assert(areEquable!(int, float)); 551 static assert(areNotEquable!(int, float)); 552 553 static assert(!areComparable!(int, string)); 554 static assert(!areEquable!(int, string)); 555 static assert(!areNotEquable!(int, string)); 556 } 557 558 enum isValueType(T) = !hasIndirections!T; 559 enum hasValueSemantics(T) = !hasIndirections!T; /+ TODO: merge with isValueType +/ 560 enum isReferenceType(T) = hasIndirections!T; 561 562 enum arityMin0(alias fun) = __traits(compiles, fun()); 563 564 /** TODO: Unite into a variadic. 565 See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org 566 */ 567 enum isCallableWith(alias fun, T) = (is(typeof(fun(T.init))) || 568 is(typeof(T.init.fun))); /+ TODO: Are both these needed? +/ 569 pure nothrow @safe @nogc unittest { 570 auto sqr(T)(T x) { return x*x; } 571 assert(isCallableWith!(sqr, int)); 572 assert(!isCallableWith!(sqr, string)); 573 } 574 575 /* TODO: Unite into a variadic. 576 See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org 577 */ 578 enum isCallableWith(alias fun, T, U) = (is(typeof(fun(T.init, 579 U.init))) || 580 is(typeof(T.init.fun(U)))); /+ TODO: Are both these needed? +/ 581 pure nothrow @safe @nogc unittest { 582 auto sqr2(T)(T x, T y) { return x*x + y*y; } 583 assert(isCallableWith!(sqr2, int, int)); 584 assert(!isCallableWith!(sqr2, int, string)); 585 } 586 587 /** Check if `T` is a Sorted Range. 588 See_Also: http://forum.dlang.org/thread/lt1g3q$15fe$1@digitalmars.com 589 */ 590 template isSortedRange(T) 591 { 592 import std.range: SortedRange; 593 enum isSortedRange = is(T == SortedRange!(_), _); /+ TODO: Or use: __traits(isSame, TemplateOf!R, SortedRange) +/ 594 } 595 596 /** Check if Function $(D expr) is callable at compile-time. 597 See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org 598 */ 599 template isCTFEable(alias fun) 600 { 601 template isCTFEable_aux(alias T) 602 { 603 enum isCTFEable_aux = T; 604 } 605 enum isCTFEable = __traits(compiles, isCTFEable_aux!(fun())); 606 } 607 608 template isCTFEable2(fun...) 609 { 610 enum isCTFEable2 = true; 611 } 612 613 pure nothrow @safe unittest { 614 int fun1() { return 1; } 615 auto fun1_N() 616 { 617 import std.array; 618 //would return Error: gc_malloc cannot be interpreted at compile time, 619 /* because it has no available source code due to a bug */ 620 return [1].array; 621 } 622 int fun2(int x) 623 { 624 return 1; 625 } 626 auto fun2_N(int x){ 627 import std.array; 628 //same as fun1_N 629 return [1].array; 630 } 631 632 int a1; 633 enum a2=0; 634 635 static assert(!isCTFEable!(()=>a1)); 636 static assert(isCTFEable!(()=>a2)); 637 638 static assert(isCTFEable!fun1); 639 /* static assert(!isCTFEable!fun1_N); */ 640 641 static assert(isCTFEable!(()=>fun2(0))); 642 /* static assert(!isCTFEable!(()=>fun2_N(0))); */ 643 //NOTE:an alternate syntax which could be implemented would be: static 644 /* assert(!isCTFEable!(fun2_N,0)); */ 645 } 646 647 /** Check if the value of $(D expr) is known at compile-time. 648 See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org 649 */ 650 enum isCTEable(alias expr) = __traits(compiles, { enum id = expr; }); 651 652 pure nothrow @safe @nogc unittest { 653 static assert(isCTEable!11); 654 enum x = 11; 655 static assert(isCTEable!x); 656 auto y = 11; 657 static assert(!isCTEable!y); 658 } 659 660 import std.traits: hasFunctionAttributes, isCallable, ParameterTypeTuple, Unqual; 661 662 /** Returns $(D true) if `T` is not $(D const) or $(D immutable). 663 Note that isConst is true for string, or immutable(char)[], because the 664 'head' is mutable. 665 */ 666 import std.traits : isMutable; 667 enum isConst(T) = !isMutable!T; 668 669 pure nothrow @safe @nogc unittest { 670 static assert(isConst!(const(int))); 671 static assert(!isConst!int); 672 } 673 674 import std.traits : CommonType; 675 676 /// Is `true` iff `Types` all share a common type. 677 enum bool haveCommonType(Types...) = !is(CommonType!Types == void); 678 679 /// 680 pure nothrow @safe @nogc unittest { 681 static assert(haveCommonType!(bool, int, long)); 682 static assert(!haveCommonType!(bool, int, string)); 683 } 684 685 /** Check if $(D fun) is a pure function. */ 686 enum bool isPure(alias fun) = hasFunctionAttributes!(fun, `pure`); 687 688 /** Check if $(D fun) is a function purely callable with arguments T. */ 689 enum bool isPurelyCallableWith(alias fun, T...) = (isPure!fun && 690 is(T == ParameterTypeTuple!fun)); 691 692 /// 693 pure nothrow @safe @nogc unittest { 694 static int foo(int x) @safe pure nothrow { return x; } 695 static assert(isPure!foo); 696 static assert(isPurelyCallableWith!(foo, int)); 697 } 698 699 /** Check if $(D fun) is a @nogc function. 700 See_Also: http://forum.dlang.org/thread/dyumjfmxmstpgyxbozry@forum.dlang.org 701 */ 702 enum bool isNogc(alias fun) = hasFunctionAttributes!(fun, `@nogc`); 703 704 /// 705 pure nothrow @safe @nogc unittest { 706 static int foo(int x) @nogc pure nothrow; 707 static int goo(int x) pure nothrow; 708 static assert(isNogc!foo); 709 static assert(!isNogc!goo); 710 } 711 712 /** Persistently Call Function $(D fun) with arguments $(D args). 713 714 Hash Id Build-Timestamp (Code-Id because we currently have stable way of hashing-algorithms) is Constructed from Data Structure: 715 - Hierarchically Mangled Unqual!typeof(instance) 716 - Use msgpack in combination with sha1Of or only sha1Of (with extended 717 overloads for sha1Of) if available. 718 719 Extend std.functional : memoize to accept pure functions that takes an 720 immutable mmap as input. Create wrapper that converts file to immutable mmap 721 and performs memoization on the pure function. 722 723 */ 724 auto persistentlyMemoizedCall(alias fun, T...)(T args) 725 if (isPure!fun && 726 isCallable!(fun, args)) 727 { 728 import std.functional: memoize; 729 return fun(args); 730 } 731 732 /** Move std.uni.newLine? 733 TODO: What to do with Windows style endings? 734 See_Also: https://en.wikipedia.org/wiki/Newline 735 */ 736 bool isNewline(C)(C c) pure nothrow @safe @nogc 737 if (isSomeChar!C) 738 { 739 import std.ascii: newline; /+ TODO: Probably not useful. +/ 740 static if (newline == "\n") 741 return (c == '\n' || c == '\r'); // optimized for systems with \n as default 742 else static if (newline == "\r") 743 return (c == '\r' || c == '\n'); // optimized for systems with \r as default 744 else 745 static assert(0, "Support Windows?"); 746 } 747 748 bool isNewline(S)(S s) pure nothrow @safe @nogc 749 if (isSomeString!S) 750 { 751 import std.ascii: newline; /+ TODO: Probably not useful. +/ 752 static if (newline == "\n") 753 return (s == '\n' || s == '\r'); // optimized for systems with \n as default 754 else static if (newline == "\r") 755 return (s == '\r' || s == '\n'); // optimized for systems with \r as default 756 else static if (newline == "\r\n") 757 return (s == "\r\n" || s == '\r' || s == '\n'); // optimized for systems with \r\n as default 758 else static if (newline == "\n\r") 759 return (s == "\n\r" || s == '\r' || s == '\n'); // optimized for systems with \n\r as default 760 else 761 static assert(0, "Support windows?"); 762 } 763 764 /** Dynamic variant of $(D EnumMembers) returning enum member constants 765 * (enumerators) of `T`. 766 * 767 * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org 768 */ 769 T[] enumMembersAsEnumerators(T)() 770 if (is(T == enum)) 771 { 772 import std.array : Appender; 773 Appender!(T[]) members; /+ TODO: use static array instead +/ 774 enum lengthMax = T.max - T.min + 1; // possibly overestimate of final length needed 775 members.reserve(lengthMax); 776 foreach (const member; __traits(allMembers, T)) 777 members.put(__traits(getMember, T, member)); 778 return members.data[]; 779 } 780 781 /** Dynamic Variant of $(D EnumMembers) excluding the enumerator aliases. 782 * 783 * See_Also: http://forum.dlang.org/post/ziappmtvucmuefphblse@forum.dlang.org 784 * See_Also: http://forum.dlang.org/post/awihyvzjswwayeqtklly@forum.dlang.org 785 * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org 786 * See_Also: https://issues.dlang.org/show_bug.cgi?id=10951 787 */ 788 auto uniqueEnumMembers(T)() @trusted 789 if (is(T == enum)) 790 { 791 import std.array : Appender; 792 Appender!(T[]) uniqueMembers; 793 enum lengthMax = T.max - T.min + 1; // possibly overestimate of final length 794 uniqueMembers.reserve(lengthMax); 795 enum maxBitCount = ((lengthMax / (8*size_t.sizeof)) + 796 (lengthMax % (8*size_t.sizeof) ? 1 : 0)); 797 size_t[maxBitCount] uniqueBits; // dense set representation of enumerators 798 foreach (const member; __traits(allMembers, T)) 799 { 800 const memberEnumerator = __traits(getMember, T, member); 801 const member_ = cast(size_t)memberEnumerator; 802 import core.bitop : bt, bts; 803 if (!bt(&uniqueBits[0], member_)) 804 { 805 uniqueMembers.put(memberEnumerator); 806 bts(&uniqueBits[0], member_); 807 } 808 } 809 return uniqueMembers.data[]; 810 } 811 812 /// 813 @safe pure nothrow /*@nogc*/ unittest { 814 enum E { x, y, z, Z = z, Y = y } 815 import std.algorithm.comparison : equal; 816 assert(enumMembersAsEnumerators!E.equal([E.x, E.y, E.z, E.Z, E.Y])); // run-time 817 assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // run-time 818 // static assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // compile-time 819 static assert(E.x == 0); 820 static assert(E.y == 1); 821 static assert(E.z == 2); 822 static assert(E.Z == E.z); 823 static assert(E.Y == E.y); 824 } 825 826 enum sizeOf(T) = T.sizeof; /+ TODO: Move to Phobos +/ 827 template sizesOf(T...) /+ TODO: Move to Phobos +/ 828 { 829 import std.meta : staticMap; 830 enum sizesOf = staticMap!(sizeOf, T); 831 } 832 833 /// 834 pure nothrow @safe unittest { 835 enum sizes = sizesOf!(bool, short, int, long); 836 837 // static use 838 static assert(sizes[0] == 1); 839 static assert(sizes[1] == 2); 840 static assert(sizes[2] == 4); 841 static assert(sizes[3] == 8); 842 843 // dynamic use 844 const i = 0; 845 assert([sizes][i] == 1); 846 } 847 848 enum stringOf(T) = T.stringof; /+ TODO: Move to Phobos +/ 849 template stringsOf(T...) /+ TODO: Move to Phobos +/ 850 { 851 import std.meta : staticMap; 852 enum stringsOf = staticMap!(stringOf, T); 853 } 854 855 /// 856 pure nothrow @safe @nogc unittest { 857 enum strings = stringsOf!(bool, short, int, long); 858 } 859 860 /** Get Dimensionality of Type `T`. 861 See_Also: http://forum.dlang.org/thread/hiuhqdxtpifhzwebewjh@forum.dlang.org?page=2 862 */ 863 864 template dimensionality (T) 865 { 866 import std.range.primitives : isInputRange; 867 template count_dim (uint i = 0) 868 { 869 static if (is(typeof(T.init.opSlice!i(0, 0)))) 870 enum count_dim = count_dim!(i+1); 871 else static if (i == 0 && 872 (isInputRange!T || 873 is(typeof(T.init[0])))) 874 enum count_dim = 1; 875 else 876 enum count_dim = i; 877 } 878 alias dimensionality = count_dim!(); 879 } 880 881 /// 882 pure nothrow @safe @nogc unittest { 883 static assert(dimensionality!(int[]) == 1); 884 } 885 886 /// Rank of type `T`. 887 private template rank(T) 888 { 889 import std.range.primitives : isInputRange; 890 static if (isInputRange!T) // is T a range? 891 enum rank = 1 + rank!(ElementType!T); // if yes, recurse 892 else 893 enum rank = 0; // base case, stop there 894 } 895 896 /// 897 pure nothrow @safe /+@nogc+/ unittest { 898 import std.range : cycle; 899 900 const d1 = [0,1]; 901 const d2 = [2,3]; 902 const d = [d1, d2]; 903 auto c = cycle(d[]); // == [[0,1],[2,3],[0,1],[2,3],[0,1]... 904 905 // TODO: assert(rank!(typeof(c)) == 1); // range of ranges. TODO: should be 2. 906 907 static assert(rank!(int[]) == 1); 908 static assert(rank!(int[][]) == 2); 909 } 910 911 /// Returns: `true` iff `T` is a template instance, `false` otherwise. 912 template isTemplateInstance(T) 913 { 914 import std.traits : TemplateOf; 915 enum isTemplateInstance = is(typeof(TemplateOf!(T))); 916 } 917 918 /// 919 pure nothrow @safe @nogc unittest { 920 struct S(T) { T x; } 921 static assert(isTemplateInstance!(S!int)); 922 static assert(!isTemplateInstance!(int)); 923 } 924 925 /** Get identifier (name) string of template instance `I`, or `null` if `I` is 926 not a template instance. */ 927 template templateIdentifierOf(I) 928 { 929 import std.traits : TemplateOf; 930 static if (isTemplateInstance!I) 931 enum templateIdentifierOf = __traits(identifier, TemplateOf!I); 932 else 933 enum templateIdentifierOf = null; 934 } 935 alias templateNameOf = templateIdentifierOf; 936 937 /// 938 pure nothrow @safe @nogc unittest { 939 struct S(T) { T x; } 940 static assert(templateIdentifierOf!(S!int) == "S"); 941 static assert(templateIdentifierOf!(int) == null); 942 } 943 944 /** Get entropy in number of bits of `T`. */ 945 template EntropyBitsOf(T) 946 { 947 import std.traits : isAggregateType, isArray; 948 static if (isAggregateType!T) 949 { 950 // foreach (memberName; __traits(allMembers, T)) // for each member name in `struct TypedKey` 951 // { 952 // const member = __traits(getMember, T.init, memberName); // member 953 // } 954 enum EntropyBitsOf = 8*T.sizeof; 955 } 956 else 957 enum EntropyBitsOf = 8*T.sizeof; 958 } 959 960 /// 961 pure nothrow @safe @nogc unittest { 962 static assert(EntropyBitsOf!int == 8*int.sizeof); 963 } 964 965 /** Is `true` if `sym` is an l-value, `false` otherwise. 966 * 967 * See_Also: https://forum.dlang.org/post/mailman.4192.1454351296.22025.digitalmars-d-learn@puremagic.com 968 * 969 * TODO: Move to Phobos 970 */ 971 enum isLvalue(alias sym) = is(typeof((ref _){}(sym))); 972 973 /// 974 pure nothrow @safe @nogc unittest { 975 int i; 976 string s; 977 static assert(isLvalue!i); 978 static assert(isLvalue!s); 979 // static assert(!isLvalue!13); 980 // static assert(!isLvalue!"a"); 981 } 982 983 template ownsItsElements(C) 984 { 985 import std.traits : hasIndirections; 986 import std.range.primitives : ElementType; 987 enum ownsItsElements = !__traits(isCopyable, C) && !hasIndirections!(ElementType!C); 988 } 989 990 /** Copied from private definition in Phobos' std.meta. 991 */ 992 private template isSame(ab...) 993 if (ab.length == 2) 994 { 995 static if (__traits(compiles, expectType!(ab[0]), 996 expectType!(ab[1]))) 997 enum isSame = is(ab[0] == ab[1]); 998 else static if (!__traits(compiles, expectType!(ab[0])) && 999 !__traits(compiles, expectType!(ab[1])) && 1000 __traits(compiles, expectBool!(ab[0] == ab[1]))) 1001 { 1002 static if (!__traits(compiles, &ab[0]) || 1003 !__traits(compiles, &ab[1])) 1004 enum isSame = (ab[0] == ab[1]); 1005 else 1006 enum isSame = __traits(isSame, ab[0], ab[1]); 1007 } 1008 else 1009 enum isSame = __traits(isSame, ab[0], ab[1]); 1010 } 1011 private template expectType(T) {} 1012 private template expectBool(bool b) {} 1013 1014 template allSatisfyIterative(alias F, T...) 1015 { 1016 static foreach (Ti; T) 1017 static if (is(typeof(allSatisfyIterative) == void) && // not yet defined 1018 !F!(Ti)) 1019 enum allSatisfyIterative = false; 1020 static if (is(typeof(allSatisfyIterative) == void)) // if not yet defined 1021 enum allSatisfyIterative = true; 1022 } 1023 1024 /// 1025 @safe unittest { 1026 import std.traits : isIntegral; 1027 1028 static assert( allSatisfyIterative!(isIntegral)); 1029 static assert( allSatisfyIterative!(isIntegral, int)); 1030 static assert(!allSatisfyIterative!(isIntegral, int, double)); 1031 static assert( allSatisfyIterative!(isIntegral, int, long)); 1032 static assert(!allSatisfyIterative!(isIntegral, string)); 1033 } 1034 1035 template anySatisfyIterative(alias F, T...) 1036 { 1037 static foreach (Ti; T) 1038 { 1039 static if (is(typeof(anySatisfyIterative) == void) && // not yet defined 1040 F!(Ti)) 1041 { 1042 enum anySatisfyIterative = true; 1043 } 1044 } 1045 static if (is(typeof(anySatisfyIterative) == void)) // if not yet defined 1046 { 1047 enum anySatisfyIterative = false; 1048 } 1049 } 1050 1051 /// 1052 @safe unittest { 1053 import std.traits : isIntegral; 1054 1055 static assert(!anySatisfyIterative!(isIntegral)); 1056 static assert( anySatisfyIterative!(isIntegral, int)); 1057 static assert(!anySatisfyIterative!(isIntegral, string, double)); 1058 static assert( anySatisfyIterative!(isIntegral, int, double)); 1059 static assert( anySatisfyIterative!(isIntegral, int, string)); 1060 } 1061 1062 version (unittest) 1063 { 1064 import std.typecons : Tuple; 1065 import nxt.array_help : s; 1066 } 1067 1068 /** Is `true` iff `T` has a property member non-function named `name`. */ 1069 template hasPropertyFunction(T, string name) 1070 { 1071 static if (__traits(hasMember, T, name)) 1072 enum hasPropertyFunction = (!is(typeof(__traits(getMember, T, name)) == function) && 1073 __traits(getOverloads, T, name).length); 1074 else 1075 enum hasPropertyFunction = false; 1076 } 1077 1078 /// 1079 unittest { 1080 struct S 1081 { 1082 int m; 1083 static int sm; 1084 1085 void f() {} 1086 static void sf() {} 1087 1088 @property int rp() { return m; } 1089 @property void wp(int) {} 1090 } 1091 1092 static assert(hasPropertyFunction!(S, "rp")); 1093 static assert(hasPropertyFunction!(S, "wp")); 1094 1095 static assert(!hasPropertyFunction!(S, "na")); 1096 static assert(!hasPropertyFunction!(S, "m")); 1097 static assert(!hasPropertyFunction!(S, "sm")); 1098 static assert(!hasPropertyFunction!(S, "f")); 1099 static assert(!hasPropertyFunction!(S, "sf")); 1100 } 1101 1102 /** Is `true` if `T.name` is a manifest constant, built-in type field, or 1103 * immutable static. 1104 */ 1105 template isManifestAssignable(T, string name) 1106 { 1107 enum isManifestAssignable = is(typeof({ enum x = mixin("T." ~ name); })); 1108 } 1109 1110 /// 1111 unittest { 1112 struct A 1113 { 1114 int m; 1115 static immutable int sim = 1; 1116 enum e = 1; 1117 } 1118 static assert(!isManifestAssignable!(A*, "na")); 1119 static assert(!isManifestAssignable!(A, "na")); 1120 static assert(!isManifestAssignable!(A, "m")); 1121 static assert(isManifestAssignable!(A, "e")); 1122 static assert(isManifestAssignable!(A, "sim")); 1123 } 1124 1125 /** Tells you if a name is a read and/or write property 1126 * 1127 * Returns: `Tuple!(bool, "isRead", bool, "isWrite")` 1128 */ 1129 auto propertySemantics(T, string name)() 1130 if (hasPropertyFunction!(T, name)) 1131 { 1132 import std.typecons : tuple; 1133 1134 enum overloads = __traits(getOverloads, T, name).length; 1135 enum canInstantiateAsField = is(typeof(mixin("T.init." ~ name))); 1136 1137 static if (overloads > 1 || canInstantiateAsField) 1138 enum canRead = true; 1139 else 1140 enum canRead = false; 1141 static if (overloads > 1 || !canInstantiateAsField) 1142 enum canWrite = true; 1143 else 1144 enum canWrite = false; 1145 1146 return tuple!("canRead", "canWrite")(canRead, canWrite); 1147 } 1148 1149 /// 1150 unittest { 1151 import std.typecons; 1152 1153 struct S 1154 { 1155 int m; 1156 @property int rp() 1157 { 1158 return m; 1159 } 1160 1161 @property void wp(int) 1162 { 1163 } 1164 1165 @property int rwp() 1166 { 1167 return m; 1168 } 1169 1170 @property void rwp(int) 1171 { 1172 } 1173 } 1174 1175 static assert(!__traits(compiles, propertySemantics!(S, "na"))); 1176 static assert(!__traits(compiles, propertySemantics!(S, "m"))); 1177 1178 static assert(propertySemantics!(S, "rp") == tuple!("canRead", "canWrite")(true, false)); 1179 static assert(propertySemantics!(S, "wp") == tuple!("canRead", "canWrite")(false, true)); 1180 static assert(propertySemantics!(S, "rwp") == tuple!("canRead", "canWrite")(true, true)); 1181 } 1182 1183 /** Is `true` iff the postblit of `T` is disabled (`this(this) @disable`). 1184 * 1185 * See_Also: https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 1186 */ 1187 template hasDisabledPostblit(T) 1188 { 1189 static if (__traits(hasMember, T, "__postblit")) 1190 enum hasDisabledPostblit = __traits(isDisabled, T.__postblit); 1191 else 1192 enum hasDisabledPostblit = false; 1193 } 1194 1195 /// 1196 pure @safe unittest { 1197 static struct S 1198 { 1199 this(this) @disable; 1200 } 1201 static assert(!hasDisabledPostblit!int); 1202 static assert( hasDisabledPostblit!S); 1203 } 1204 1205 template isSubclassOf(Class, BaseClass) 1206 { 1207 import std.traits : BaseClassesTuple; 1208 alias BaseClasses = BaseClassesTuple!Class; 1209 import std.meta : staticIndexOf; 1210 enum isSubclassOf = staticIndexOf!(BaseClass, BaseClasses) != -1; 1211 } 1212 1213 /// 1214 pure @safe unittest { 1215 class X {} 1216 class Y : X {} 1217 1218 static assert(!isSubclassOf!(X, Y)); 1219 static assert( isSubclassOf!(X, Object)); 1220 1221 static assert( isSubclassOf!(Y, X)); 1222 static assert( isSubclassOf!(Y, Object)); 1223 } 1224 1225 /** Is `true` iff F are all. 1226 * 1227 * See_Also: https://forum.dlang.org/post/p9orut$2h4$1@digitalmars.com 1228 */ 1229 bool isStronglyPure(alias F)() 1230 { 1231 import std.traits : Parameters, ParameterDefaults; 1232 import std.algorithm : canFind; 1233 static if (!__traits(getFunctionAttributes, F).canFind("pure")) /+ TODO: use staticIndexOf +/ 1234 return false; 1235 enum length = ParameterDefaults!F.length; 1236 alias P = Parameters!F; 1237 static foreach (i; 0 .. length) 1238 { 1239 immutable c = __traits(getParameterStorageClasses, F, i); 1240 /* Why does this print: 1241 const(int) int true (d-dmd-all) 1242 const(int) int true (d-dmd-all) 1243 const(int) int true (d-dmd-all) 1244 ? 1245 */ 1246 // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", const(P[i]).stringof, " ", P[i].stringof, " ", is(P[i] == const(P[i]))); 1247 // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", is(int == const(int))); 1248 if (c.canFind("ref", "out") && /+ TODO: use staticIndexOf +/ 1249 is(const(P[i]) == P[i])) 1250 return false; 1251 } 1252 return true; 1253 } 1254 1255 unittest { 1256 static int f(int x) pure { return x + 1; } 1257 static int g(ref int x) pure { return x + 1; } 1258 static int gc(const ref int x) pure { return x + 1; } 1259 static int gi(immutable ref int x) pure { return x + 1; } 1260 static void h(out int x) pure { x = 0; } 1261 static assert(isStronglyPure!f); 1262 static assert(!isStronglyPure!g); 1263 static assert(!isStronglyPure!h); 1264 /+ TODO: static assert(isStronglyPure!gc); +/ 1265 /+ TODO: static assert(isStronglyPure!gi); +/ 1266 } 1267 1268 /** Is `true` iff `F` returns a value whose mutable indirections are unique. 1269 * 1270 * TODO: Check if the uniqueness property only applies to the first indirection layer. 1271 * TODO: Move to Phobos std.traits. 1272 * 1273 * See_Also: https://dlang.org/changelog/2.101.0.html#dmd.fix-immutable-conv 1274 */ 1275 enum returnsUnique(alias F) = __traits(compiles, { 1276 import std.traits : Parameters; 1277 1278 alias P = Parameters!(F); 1279 P args = P.init; 1280 1281 static if (__traits(isPOD, P)) { 1282 auto m = F(args); 1283 immutable i = F(args); 1284 } else { 1285 import core.lifetime : move; 1286 auto m = F(move(args)); 1287 immutable i = F(move(args)); 1288 } 1289 1290 /* 1291 Doesn’t suffice to pass `P.init` here because dmd knows that passing 1292 `null` to `x` in 1293 1294 static int* f(scope return int *x) pure nothrow @safe @nogc { return x; } 1295 1296 is strongly pure. 1297 */ 1298 static assert(!is(typeof(m) == typeof(i))); 1299 }); 1300 1301 alias hasUniqueReturn = returnsUnique; 1302 1303 /// 1304 pure nothrow @safe @nogc unittest { 1305 import core.memory : pureMalloc; 1306 1307 static int identity(int x) @safe nothrow @nogc { return x; } 1308 static int identityP(int x) pure nothrow @safe @nogc { return x; } 1309 static assert( returnsUnique!identity); 1310 static assert( returnsUnique!identityP); 1311 1312 static auto makeBytes(size_t n) @safe nothrow @nogc { return pureMalloc(n); } 1313 static auto makeBytesP(size_t n) pure nothrow @safe @nogc { return pureMalloc(n); } 1314 static assert( returnsUnique!makeBytes); 1315 static assert( returnsUnique!makeBytesP); 1316 1317 static int* f(scope return int *x) pure nothrow @safe @nogc { return x; } 1318 static assert(!returnsUnique!f); 1319 1320 struct Sint { int x; } 1321 static Sint fS(scope return Sint x) pure nothrow @safe @nogc { return x; } 1322 static assert( returnsUnique!fS); 1323 1324 struct Sintp { int* x; } 1325 static Sintp fSintp(scope return Sintp x) pure nothrow @safe @nogc { return x; } 1326 static assert(!returnsUnique!fSintp); 1327 1328 struct S3 { immutable int* x; } 1329 static S3 fS3(scope return S3 x) pure nothrow @safe @nogc { return x; } 1330 static assert( returnsUnique!fS3); 1331 }