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 x = [1, 2, 3].s[].assumeSorted; 520 static assert(is(typeof(x) == SortedRange!(_), _...)); 521 static assert(isA!(SortedRange, typeof(x))); 522 } 523 524 /** See_Also: http://forum.dlang.org/thread/bug-6384-3@http.d.puremagic.com/issues/ 525 See_Also: http://forum.dlang.org/thread/jrqiiicmtpenzokfxvlz@forum.dlang.org */ 526 enum isOpBinary(T, string op, U) = is(typeof(mixin("T.init" ~ op ~ "U.init"))); 527 528 enum isComparable(T) = is(typeof({ return T.init < T.init; })); //+ TODO: Move to Phobos std.traits +/ 529 enum isEquable (T) = is(typeof({ return T.init == T.init; })); //+ TODO: Move to Phobos std.traits +/ 530 enum isNotEquable(T) = is(typeof({ return T.init != T.init; })); //+ TODO: Move to Phobos std.traits +/ 531 532 pure nothrow @safe @nogc unittest { 533 static assert(isComparable!int); 534 static assert(isComparable!string); 535 static assert(!isComparable!creal); 536 static struct Foo {} 537 static assert(!isComparable!Foo); 538 static struct Bar { bool opCmp(Bar) { return true; } } 539 static assert(isComparable!Bar); 540 } 541 542 /+ TODO: variadic +/ 543 enum areComparable(T, U) = is(typeof({ return T.init < U.init; })); //+ TODO: Move to Phobos std.traits +/ 544 enum areEquable (T, U) = is(typeof({ return T.init == U.init; })); //+ TODO: Move to Phobos std.traits +/ 545 enum areNotEquable(T, U) = is(typeof({ return T.init != U.init; })); //+ TODO: Move to Phobos std.traits +/ 546 547 pure nothrow @safe @nogc unittest { 548 static assert(areComparable!(int, float)); 549 static assert(areEquable!(int, float)); 550 static assert(areNotEquable!(int, float)); 551 552 static assert(!areComparable!(int, string)); 553 static assert(!areEquable!(int, string)); 554 static assert(!areNotEquable!(int, string)); 555 } 556 557 enum isValueType(T) = !hasIndirections!T; 558 enum hasValueSemantics(T) = !hasIndirections!T; /+ TODO: merge with isValueType +/ 559 enum isReferenceType(T) = hasIndirections!T; 560 561 enum arityMin0(alias fun) = __traits(compiles, fun()); 562 563 /** TODO: Unite into a variadic. 564 See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org 565 */ 566 enum isCallableWith(alias fun, T) = (is(typeof(fun(T.init))) || 567 is(typeof(T.init.fun))); /+ TODO: Are both these needed? +/ 568 pure nothrow @safe @nogc unittest { 569 auto sqr(T)(T x) { return x*x; } 570 assert(isCallableWith!(sqr, int)); 571 assert(!isCallableWith!(sqr, string)); 572 } 573 574 /* TODO: Unite into a variadic. 575 See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org 576 */ 577 enum isCallableWith(alias fun, T, U) = (is(typeof(fun(T.init, 578 U.init))) || 579 is(typeof(T.init.fun(U)))); /+ TODO: Are both these needed? +/ 580 pure nothrow @safe @nogc unittest { 581 auto sqr2(T)(T x, T y) { return x*x + y*y; } 582 assert(isCallableWith!(sqr2, int, int)); 583 assert(!isCallableWith!(sqr2, int, string)); 584 } 585 586 /** Check if `T` is a Sorted Range. 587 See_Also: http://forum.dlang.org/thread/lt1g3q$15fe$1@digitalmars.com 588 */ 589 template isSortedRange(T) 590 { 591 import std.range: SortedRange; 592 enum isSortedRange = is(T == SortedRange!(_), _); /+ TODO: Or use: __traits(isSame, TemplateOf!R, SortedRange) +/ 593 } 594 595 /** Check if Function $(D expr) is callable at compile-time. 596 See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org 597 */ 598 template isCTFEable(alias fun) 599 { 600 template isCTFEable_aux(alias T) 601 { 602 enum isCTFEable_aux = T; 603 } 604 enum isCTFEable = __traits(compiles, isCTFEable_aux!(fun())); 605 } 606 607 template isCTFEable2(fun...) 608 { 609 enum isCTFEable2 = true; 610 } 611 612 pure nothrow @safe unittest { 613 int fun1() { return 1; } 614 auto fun1_N() 615 { 616 import std.array; 617 //would return Error: gc_malloc cannot be interpreted at compile time, 618 /* because it has no available source code due to a bug */ 619 return [1].array; 620 } 621 int fun2(int x) 622 { 623 return 1; 624 } 625 auto fun2_N(int x){ 626 import std.array; 627 //same as fun1_N 628 return [1].array; 629 } 630 631 int a1; 632 enum a2=0; 633 634 static assert(!isCTFEable!(()=>a1)); 635 static assert(isCTFEable!(()=>a2)); 636 637 static assert(isCTFEable!fun1); 638 /* static assert(!isCTFEable!fun1_N); */ 639 640 static assert(isCTFEable!(()=>fun2(0))); 641 /* static assert(!isCTFEable!(()=>fun2_N(0))); */ 642 //NOTE:an alternate syntax which could be implemented would be: static 643 /* assert(!isCTFEable!(fun2_N,0)); */ 644 } 645 646 /** Check if the value of $(D expr) is known at compile-time. 647 See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org 648 */ 649 enum isCTEable(alias expr) = __traits(compiles, { enum id = expr; }); 650 651 pure nothrow @safe @nogc unittest { 652 static assert(isCTEable!11); 653 enum x = 11; 654 static assert(isCTEable!x); 655 auto y = 11; 656 static assert(!isCTEable!y); 657 } 658 659 import std.traits: hasFunctionAttributes, isCallable, ParameterTypeTuple, Unqual; 660 661 /** Returns $(D true) if `T` is not $(D const) or $(D immutable). 662 Note that isConst is true for string, or immutable(char)[], because the 663 'head' is mutable. 664 */ 665 import std.traits : isMutable; 666 enum isConst(T) = !isMutable!T; 667 668 pure nothrow @safe @nogc unittest { 669 static assert(isConst!(const(int))); 670 static assert(!isConst!int); 671 } 672 673 import std.traits : CommonType; 674 675 /// Is `true` iff `Types` all share a common type. 676 enum bool haveCommonType(Types...) = !is(CommonType!Types == void); 677 678 /// 679 pure nothrow @safe @nogc unittest { 680 static assert(haveCommonType!(bool, int, long)); 681 static assert(!haveCommonType!(bool, int, string)); 682 } 683 684 /** Check if $(D fun) is a pure function. */ 685 enum bool isPure(alias fun) = hasFunctionAttributes!(fun, `pure`); 686 687 /** Check if $(D fun) is a function purely callable with arguments T. */ 688 enum bool isPurelyCallableWith(alias fun, T...) = (isPure!fun && 689 is(T == ParameterTypeTuple!fun)); 690 691 /// 692 pure nothrow @safe @nogc unittest { 693 static int foo(int x) @safe pure nothrow { return x; } 694 static assert(isPure!foo); 695 static assert(isPurelyCallableWith!(foo, int)); 696 } 697 698 /** Check if $(D fun) is a @nogc function. 699 See_Also: http://forum.dlang.org/thread/dyumjfmxmstpgyxbozry@forum.dlang.org 700 */ 701 enum bool isNogc(alias fun) = hasFunctionAttributes!(fun, `@nogc`); 702 703 /// 704 pure nothrow @safe @nogc unittest { 705 static int foo(int x) @nogc pure nothrow; 706 static int goo(int x) pure nothrow; 707 static assert(isNogc!foo); 708 static assert(!isNogc!goo); 709 } 710 711 /** Persistently Call Function $(D fun) with arguments $(D args). 712 713 Hash Id Build-Timestamp (Code-Id because we currently have stable way of hashing-algorithms) is Constructed from Data Structure: 714 - Hierarchically Mangled Unqual!typeof(instance) 715 - Use msgpack in combination with sha1Of or only sha1Of (with extended 716 overloads for sha1Of) if available. 717 718 Extend std.functional : memoize to accept pure functions that takes an 719 immutable mmap as input. Create wrapper that converts file to immutable mmap 720 and performs memoization on the pure function. 721 722 */ 723 auto persistentlyMemoizedCall(alias fun, T...)(T args) 724 if (isPure!fun && 725 isCallable!(fun, args)) 726 { 727 import std.functional: memoize; 728 return fun(args); 729 } 730 731 /** Move std.uni.newLine? 732 TODO: What to do with Windows style endings? 733 See_Also: https://en.wikipedia.org/wiki/Newline 734 */ 735 bool isNewline(C)(C c) pure nothrow @safe @nogc 736 if (isSomeChar!C) 737 { 738 import std.ascii: newline; /+ TODO: Probably not useful. +/ 739 static if (newline == "\n") 740 return (c == '\n' || c == '\r'); // optimized for systems with \n as default 741 else static if (newline == "\r") 742 return (c == '\r' || c == '\n'); // optimized for systems with \r as default 743 else 744 static assert(0, "Support Windows?"); 745 } 746 747 bool isNewline(S)(S s) pure nothrow @safe @nogc 748 if (isSomeString!S) 749 { 750 import std.ascii: newline; /+ TODO: Probably not useful. +/ 751 static if (newline == "\n") 752 return (s == '\n' || s == '\r'); // optimized for systems with \n as default 753 else static if (newline == "\r") 754 return (s == '\r' || s == '\n'); // optimized for systems with \r as default 755 else static if (newline == "\r\n") 756 return (s == "\r\n" || s == '\r' || s == '\n'); // optimized for systems with \r\n as default 757 else static if (newline == "\n\r") 758 return (s == "\n\r" || s == '\r' || s == '\n'); // optimized for systems with \n\r as default 759 else 760 static assert(0, "Support windows?"); 761 } 762 763 /** Dynamic variant of $(D EnumMembers) returning enum member constants 764 * (enumerators) of `T`. 765 * 766 * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org 767 */ 768 T[] enumMembersAsEnumerators(T)() 769 if (is(T == enum)) 770 { 771 import std.array : Appender; 772 Appender!(T[]) members; /+ TODO: use static array instead +/ 773 enum lengthMax = T.max - T.min + 1; // possibly overestimate of final length needed 774 members.reserve(lengthMax); 775 foreach (const member; __traits(allMembers, T)) 776 members.put(__traits(getMember, T, member)); 777 return members.data[]; 778 } 779 780 /** Dynamic Variant of $(D EnumMembers) excluding the enumerator aliases. 781 * 782 * See_Also: http://forum.dlang.org/post/ziappmtvucmuefphblse@forum.dlang.org 783 * See_Also: http://forum.dlang.org/post/awihyvzjswwayeqtklly@forum.dlang.org 784 * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org 785 * See_Also: https://issues.dlang.org/show_bug.cgi?id=10951 786 */ 787 auto uniqueEnumMembers(T)() @trusted 788 if (is(T == enum)) 789 { 790 import std.array : Appender; 791 Appender!(T[]) uniqueMembers; 792 enum lengthMax = T.max - T.min + 1; // possibly overestimate of final length 793 uniqueMembers.reserve(lengthMax); 794 enum maxBitCount = ((lengthMax / (8*size_t.sizeof)) + 795 (lengthMax % (8*size_t.sizeof) ? 1 : 0)); 796 size_t[maxBitCount] uniqueBits; // dense set representation of enumerators 797 foreach (const member; __traits(allMembers, T)) 798 { 799 const memberEnumerator = __traits(getMember, T, member); 800 const member_ = cast(size_t)memberEnumerator; 801 import core.bitop : bt, bts; 802 if (!bt(&uniqueBits[0], member_)) 803 { 804 uniqueMembers.put(memberEnumerator); 805 bts(&uniqueBits[0], member_); 806 } 807 } 808 return uniqueMembers.data[]; 809 } 810 811 /// 812 @safe pure nothrow /*@nogc*/ unittest { 813 enum E { x, y, z, Z = z, Y = y } 814 import std.algorithm.comparison : equal; 815 assert(enumMembersAsEnumerators!E.equal([E.x, E.y, E.z, E.Z, E.Y])); // run-time 816 assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // run-time 817 // static assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // compile-time 818 static assert(E.x == 0); 819 static assert(E.y == 1); 820 static assert(E.z == 2); 821 static assert(E.Z == E.z); 822 static assert(E.Y == E.y); 823 } 824 825 enum sizeOf(T) = T.sizeof; /+ TODO: Move to Phobos +/ 826 template sizesOf(T...) /+ TODO: Move to Phobos +/ 827 { 828 import std.meta : staticMap; 829 enum sizesOf = staticMap!(sizeOf, T); 830 } 831 832 /// 833 pure nothrow @safe unittest { 834 enum sizes = sizesOf!(bool, short, int, long); 835 836 // static use 837 static assert(sizes[0] == 1); 838 static assert(sizes[1] == 2); 839 static assert(sizes[2] == 4); 840 static assert(sizes[3] == 8); 841 842 // dynamic use 843 const i = 0; 844 assert([sizes][i] == 1); 845 } 846 847 enum stringOf(T) = T.stringof; /+ TODO: Move to Phobos +/ 848 template stringsOf(T...) /+ TODO: Move to Phobos +/ 849 { 850 import std.meta : staticMap; 851 enum stringsOf = staticMap!(stringOf, T); 852 } 853 854 /// 855 pure nothrow @safe @nogc unittest { 856 enum strings = stringsOf!(bool, short, int, long); 857 } 858 859 /** Get Dimensionality of Type `T`. 860 See_Also: http://forum.dlang.org/thread/hiuhqdxtpifhzwebewjh@forum.dlang.org?page=2 861 */ 862 863 template dimensionality (T) 864 { 865 import std.range.primitives : isInputRange; 866 template count_dim (uint i = 0) 867 { 868 static if (is(typeof(T.init.opSlice!i(0, 0)))) 869 enum count_dim = count_dim!(i+1); 870 else static if (i == 0 && 871 (isInputRange!T || 872 is(typeof(T.init[0])))) 873 enum count_dim = 1; 874 else 875 enum count_dim = i; 876 } 877 alias dimensionality = count_dim!(); 878 } 879 880 /// 881 pure nothrow @safe @nogc unittest { 882 static assert(dimensionality!(int[]) == 1); 883 } 884 885 /// Rank of type `T`. 886 template rank(T) 887 { 888 import std.range.primitives : isInputRange; 889 static if (isInputRange!T) // is T a range? 890 enum rank = 1 + rank!(ElementType!T); // if yes, recurse 891 else 892 enum rank = 0; // base case, stop there 893 } 894 895 /// 896 pure nothrow @safe @nogc unittest { 897 import std.range : cycle; 898 899 auto c = cycle([[0,1].s[], 900 [2,3].s[]].s[]); // == [[0,1],[2,3],[0,1],[2,3],[0,1]... 901 902 assert(rank!(typeof(c)) == 2); // range of ranges 903 904 static assert(rank!(int[]) == 1); 905 static assert(rank!(int[][]) == 2); 906 } 907 908 /// Returns: `true` iff `T` is a template instance, `false` otherwise. 909 template isTemplateInstance(T) 910 { 911 import std.traits : TemplateOf; 912 enum isTemplateInstance = is(typeof(TemplateOf!(T))); 913 } 914 915 /// 916 pure nothrow @safe @nogc unittest { 917 struct S(T) { T x; } 918 static assert(isTemplateInstance!(S!int)); 919 static assert(!isTemplateInstance!(int)); 920 } 921 922 /** Get identifier (name) string of template instance `I`, or `null` if `I` is 923 not a template instance. */ 924 template templateIdentifierOf(I) 925 { 926 import std.traits : TemplateOf; 927 static if (isTemplateInstance!I) 928 enum templateIdentifierOf = __traits(identifier, TemplateOf!I); 929 else 930 enum templateIdentifierOf = null; 931 } 932 alias templateNameOf = templateIdentifierOf; 933 934 /// 935 pure nothrow @safe @nogc unittest { 936 struct S(T) { T x; } 937 static assert(templateIdentifierOf!(S!int) == "S"); 938 static assert(templateIdentifierOf!(int) == null); 939 } 940 941 /** Get entropy in number of bits of `T`. */ 942 template EntropyBitsOf(T) 943 { 944 import std.traits : isAggregateType, isArray; 945 static if (isAggregateType!T) 946 { 947 // foreach (memberName; __traits(allMembers, T)) // for each member name in `struct TypedKey` 948 // { 949 // const member = __traits(getMember, T.init, memberName); // member 950 // } 951 enum EntropyBitsOf = 8*T.sizeof; 952 } 953 else 954 enum EntropyBitsOf = 8*T.sizeof; 955 } 956 957 /// 958 pure nothrow @safe @nogc unittest { 959 static assert(EntropyBitsOf!int == 8*int.sizeof); 960 } 961 962 /** Is `true` if `sym` is an l-value, `false` otherwise. 963 * 964 * See_Also: https://forum.dlang.org/post/mailman.4192.1454351296.22025.digitalmars-d-learn@puremagic.com 965 * 966 * TODO: Move to Phobos 967 */ 968 enum isLvalue(alias sym) = is(typeof((ref _){}(sym))); 969 970 /// 971 pure nothrow @safe @nogc unittest { 972 int i; 973 string s; 974 static assert(isLvalue!i); 975 static assert(isLvalue!s); 976 // static assert(!isLvalue!13); 977 // static assert(!isLvalue!"a"); 978 } 979 980 template ownsItsElements(C) 981 { 982 import std.traits : hasIndirections; 983 import std.range.primitives : ElementType; 984 enum ownsItsElements = !__traits(isCopyable, C) && !hasIndirections!(ElementType!C); 985 } 986 987 /** Copied from private definition in Phobos' std.meta. 988 */ 989 private template isSame(ab...) 990 if (ab.length == 2) 991 { 992 static if (__traits(compiles, expectType!(ab[0]), 993 expectType!(ab[1]))) 994 enum isSame = is(ab[0] == ab[1]); 995 else static if (!__traits(compiles, expectType!(ab[0])) && 996 !__traits(compiles, expectType!(ab[1])) && 997 __traits(compiles, expectBool!(ab[0] == ab[1]))) 998 { 999 static if (!__traits(compiles, &ab[0]) || 1000 !__traits(compiles, &ab[1])) 1001 enum isSame = (ab[0] == ab[1]); 1002 else 1003 enum isSame = __traits(isSame, ab[0], ab[1]); 1004 } 1005 else 1006 enum isSame = __traits(isSame, ab[0], ab[1]); 1007 } 1008 private template expectType(T) {} 1009 private template expectBool(bool b) {} 1010 1011 template allSatisfyIterative(alias F, T...) 1012 { 1013 static foreach (Ti; T) 1014 static if (is(typeof(allSatisfyIterative) == void) && // not yet defined 1015 !F!(Ti)) 1016 enum allSatisfyIterative = false; 1017 static if (is(typeof(allSatisfyIterative) == void)) // if not yet defined 1018 enum allSatisfyIterative = true; 1019 } 1020 1021 /// 1022 @safe unittest { 1023 import std.traits : isIntegral; 1024 1025 static assert( allSatisfyIterative!(isIntegral)); 1026 static assert( allSatisfyIterative!(isIntegral, int)); 1027 static assert(!allSatisfyIterative!(isIntegral, int, double)); 1028 static assert( allSatisfyIterative!(isIntegral, int, long)); 1029 static assert(!allSatisfyIterative!(isIntegral, string)); 1030 } 1031 1032 template anySatisfyIterative(alias F, T...) 1033 { 1034 static foreach (Ti; T) 1035 { 1036 static if (is(typeof(anySatisfyIterative) == void) && // not yet defined 1037 F!(Ti)) 1038 { 1039 enum anySatisfyIterative = true; 1040 } 1041 } 1042 static if (is(typeof(anySatisfyIterative) == void)) // if not yet defined 1043 { 1044 enum anySatisfyIterative = false; 1045 } 1046 } 1047 1048 /// 1049 @safe unittest { 1050 import std.traits : isIntegral; 1051 1052 static assert(!anySatisfyIterative!(isIntegral)); 1053 static assert( anySatisfyIterative!(isIntegral, int)); 1054 static assert(!anySatisfyIterative!(isIntegral, string, double)); 1055 static assert( anySatisfyIterative!(isIntegral, int, double)); 1056 static assert( anySatisfyIterative!(isIntegral, int, string)); 1057 } 1058 1059 version (unittest) 1060 { 1061 import std.typecons : Tuple; 1062 import nxt.array_help : s; 1063 } 1064 1065 /** Is `true` iff `T` has a property member non-function named `name`. */ 1066 template hasPropertyFunction(T, string name) 1067 { 1068 static if (__traits(hasMember, T, name)) 1069 enum hasPropertyFunction = (!is(typeof(__traits(getMember, T, name)) == function) && 1070 __traits(getOverloads, T, name).length); 1071 else 1072 enum hasPropertyFunction = false; 1073 } 1074 1075 /// 1076 unittest { 1077 struct S 1078 { 1079 int m; 1080 static int sm; 1081 1082 void f() {} 1083 static void sf() {} 1084 1085 @property int rp() { return m; } 1086 @property void wp(int) {} 1087 } 1088 1089 static assert(hasPropertyFunction!(S, "rp")); 1090 static assert(hasPropertyFunction!(S, "wp")); 1091 1092 static assert(!hasPropertyFunction!(S, "na")); 1093 static assert(!hasPropertyFunction!(S, "m")); 1094 static assert(!hasPropertyFunction!(S, "sm")); 1095 static assert(!hasPropertyFunction!(S, "f")); 1096 static assert(!hasPropertyFunction!(S, "sf")); 1097 } 1098 1099 /** Is `true` if `T.name` is a manifest constant, built-in type field, or 1100 * immutable static. 1101 */ 1102 template isManifestAssignable(T, string name) 1103 { 1104 enum isManifestAssignable = is(typeof({ enum x = mixin("T." ~ name); })); 1105 } 1106 1107 /// 1108 unittest { 1109 struct A 1110 { 1111 int m; 1112 static immutable int sim = 1; 1113 enum e = 1; 1114 } 1115 static assert(!isManifestAssignable!(A*, "na")); 1116 static assert(!isManifestAssignable!(A, "na")); 1117 static assert(!isManifestAssignable!(A, "m")); 1118 static assert(isManifestAssignable!(A, "e")); 1119 static assert(isManifestAssignable!(A, "sim")); 1120 } 1121 1122 /** Tells you if a name is a read and/or write property 1123 * 1124 * Returns: `Tuple!(bool, "isRead", bool, "isWrite")` 1125 */ 1126 auto propertySemantics(T, string name)() 1127 if (hasPropertyFunction!(T, name)) 1128 { 1129 import std.typecons : tuple; 1130 1131 enum overloads = __traits(getOverloads, T, name).length; 1132 enum canInstantiateAsField = is(typeof(mixin("T.init." ~ name))); 1133 1134 static if (overloads > 1 || canInstantiateAsField) 1135 enum canRead = true; 1136 else 1137 enum canRead = false; 1138 static if (overloads > 1 || !canInstantiateAsField) 1139 enum canWrite = true; 1140 else 1141 enum canWrite = false; 1142 1143 return tuple!("canRead", "canWrite")(canRead, canWrite); 1144 } 1145 1146 /// 1147 unittest { 1148 import std.typecons; 1149 1150 struct S 1151 { 1152 int m; 1153 @property int rp() 1154 { 1155 return m; 1156 } 1157 1158 @property void wp(int) 1159 { 1160 } 1161 1162 @property int rwp() 1163 { 1164 return m; 1165 } 1166 1167 @property void rwp(int) 1168 { 1169 } 1170 } 1171 1172 static assert(!__traits(compiles, propertySemantics!(S, "na"))); 1173 static assert(!__traits(compiles, propertySemantics!(S, "m"))); 1174 1175 static assert(propertySemantics!(S, "rp") == tuple!("canRead", "canWrite")(true, false)); 1176 static assert(propertySemantics!(S, "wp") == tuple!("canRead", "canWrite")(false, true)); 1177 static assert(propertySemantics!(S, "rwp") == tuple!("canRead", "canWrite")(true, true)); 1178 } 1179 1180 /** Is `true` iff the postblit of `T` is disabled (`this(this) @disable`). 1181 * 1182 * See_Also: https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 1183 */ 1184 template hasDisabledPostblit(T) 1185 { 1186 static if (__traits(hasMember, T, "__postblit")) 1187 enum hasDisabledPostblit = __traits(isDisabled, T.__postblit); 1188 else 1189 enum hasDisabledPostblit = false; 1190 } 1191 1192 /// 1193 pure @safe unittest { 1194 static struct S 1195 { 1196 this(this) @disable; 1197 } 1198 static assert(!hasDisabledPostblit!int); 1199 static assert( hasDisabledPostblit!S); 1200 } 1201 1202 template isSubclassOf(Class, BaseClass) 1203 { 1204 import std.traits : BaseClassesTuple; 1205 alias BaseClasses = BaseClassesTuple!Class; 1206 import std.meta : staticIndexOf; 1207 enum isSubclassOf = staticIndexOf!(BaseClass, BaseClasses) != -1; 1208 } 1209 1210 /// 1211 pure @safe unittest { 1212 class X {} 1213 class Y : X {} 1214 1215 static assert(!isSubclassOf!(X, Y)); 1216 static assert( isSubclassOf!(X, Object)); 1217 1218 static assert( isSubclassOf!(Y, X)); 1219 static assert( isSubclassOf!(Y, Object)); 1220 } 1221 1222 /** Is `true` iff F are all. 1223 * 1224 * See_Also: https://forum.dlang.org/post/p9orut$2h4$1@digitalmars.com 1225 */ 1226 bool isStronglyPure(alias F)() 1227 { 1228 import std.traits : Parameters, ParameterDefaults; 1229 import std.algorithm : canFind; 1230 static if (!__traits(getFunctionAttributes, F).canFind("pure")) /+ TODO: use staticIndexOf +/ 1231 return false; 1232 enum length = ParameterDefaults!F.length; 1233 alias P = Parameters!F; 1234 static foreach (i; 0 .. length) 1235 { 1236 immutable c = __traits(getParameterStorageClasses, F, i); 1237 /* Why does this print: 1238 const(int) int true (d-dmd-all) 1239 const(int) int true (d-dmd-all) 1240 const(int) int true (d-dmd-all) 1241 ? 1242 */ 1243 // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", const(P[i]).stringof, " ", P[i].stringof, " ", is(P[i] == const(P[i]))); 1244 // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", is(int == const(int))); 1245 if (c.canFind("ref", "out") && /+ TODO: use staticIndexOf +/ 1246 is(const(P[i]) == P[i])) 1247 return false; 1248 } 1249 return true; 1250 } 1251 1252 unittest { 1253 static int f(int x) pure { return x + 1; } 1254 static int g(ref int x) pure { return x + 1; } 1255 static int gc(const ref int x) pure { return x + 1; } 1256 static int gi(immutable ref int x) pure { return x + 1; } 1257 static void h(out int x) pure { x = 0; } 1258 static assert(isStronglyPure!f); 1259 static assert(!isStronglyPure!g); 1260 static assert(!isStronglyPure!h); 1261 /+ TODO: static assert(isStronglyPure!gc); +/ 1262 /+ TODO: static assert(isStronglyPure!gi); +/ 1263 } 1264 1265 /** Is `true` iff `F` returns a value whose mutable indirections are unique. 1266 * 1267 * TODO: Check if the uniqueness property only applies to the first indirection layer. 1268 * TODO: Move to Phobos std.traits. 1269 * 1270 * See_Also: https://dlang.org/changelog/2.101.0.html#dmd.fix-immutable-conv 1271 */ 1272 enum returnsUnique(alias F) = __traits(compiles, { 1273 import std.traits : Parameters; 1274 1275 alias P = Parameters!(F); 1276 P args = P.init; 1277 1278 static if (__traits(isPOD, P)) { 1279 auto m = F(args); 1280 immutable i = F(args); 1281 } else { 1282 import core.lifetime : move; 1283 auto m = F(move(args)); 1284 immutable i = F(move(args)); 1285 } 1286 1287 /* 1288 Doesn’t suffice to pass `P.init` here because dmd knows that passing 1289 `null` to `x` in 1290 1291 static int* f(scope return int *x) pure nothrow @safe @nogc { return x; } 1292 1293 is strongly pure. 1294 */ 1295 static assert(!is(typeof(m) == typeof(i))); 1296 }); 1297 1298 alias hasUniqueReturn = returnsUnique; 1299 1300 /// 1301 pure nothrow @safe @nogc unittest { 1302 import core.memory : pureMalloc; 1303 1304 static int identity(int x) @safe nothrow @nogc { return x; } 1305 static int identityP(int x) pure nothrow @safe @nogc { return x; } 1306 static assert( returnsUnique!identity); 1307 static assert( returnsUnique!identityP); 1308 1309 static auto makeBytes(size_t n) @safe nothrow @nogc { return pureMalloc(n); } 1310 static auto makeBytesP(size_t n) pure nothrow @safe @nogc { return pureMalloc(n); } 1311 static assert( returnsUnique!makeBytes); 1312 static assert( returnsUnique!makeBytesP); 1313 1314 static int* f(scope return int *x) pure nothrow @safe @nogc { return x; } 1315 static assert(!returnsUnique!f); 1316 1317 struct Sint { int x; } 1318 static Sint fS(scope return Sint x) pure nothrow @safe @nogc { return x; } 1319 static assert( returnsUnique!fS); 1320 1321 struct Sintp { int* x; } 1322 static Sintp fSintp(scope return Sintp x) pure nothrow @safe @nogc { return x; } 1323 static assert(!returnsUnique!fSintp); 1324 1325 struct S3 { immutable int* x; } 1326 static S3 fS3(scope return S3 x) pure nothrow @safe @nogc { return x; } 1327 static assert( returnsUnique!fS3); 1328 }