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