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