| /** |
| * Contains traits for runtime internal usage. |
| * |
| * Copyright: Copyright Digital Mars 2014 -. |
| * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). |
| * Authors: Martin Nowak |
| * Source: $(DRUNTIMESRC core/internal/_traits.d) |
| */ |
| module core.internal.traits; |
| |
| alias AliasSeq(TList...) = TList; |
| |
| template Fields(T) |
| { |
| static if (is(T == struct) || is(T == union)) |
| alias Fields = typeof(T.tupleof[0 .. $ - __traits(isNested, T)]); |
| else static if (is(T == class) || is(T == interface)) |
| alias Fields = typeof(T.tupleof); |
| else |
| alias Fields = AliasSeq!T; |
| } |
| |
| T trustedCast(T, U)(auto ref U u) @trusted pure nothrow |
| { |
| return cast(T)u; |
| } |
| |
| alias Unconst(T : const U, U) = U; |
| |
| /// taken from std.traits.Unqual |
| template Unqual(T : const U, U) |
| { |
| static if (is(U == shared V, V)) |
| alias Unqual = V; |
| else |
| alias Unqual = U; |
| } |
| |
| template BaseElemOf(T) |
| { |
| static if (is(OriginalType!T == E[N], E, size_t N)) |
| alias BaseElemOf = BaseElemOf!E; |
| else |
| alias BaseElemOf = T; |
| } |
| |
| unittest |
| { |
| static assert(is(BaseElemOf!(int) == int)); |
| static assert(is(BaseElemOf!(int[1]) == int)); |
| static assert(is(BaseElemOf!(int[1][2]) == int)); |
| static assert(is(BaseElemOf!(int[1][]) == int[1][])); |
| static assert(is(BaseElemOf!(int[][1]) == int[])); |
| enum E : int[2]{ test = [0, 1] } |
| static assert(is(BaseElemOf!(E) == int)); |
| } |
| |
| // [For internal use] |
| template ModifyTypePreservingTQ(alias Modifier, T) |
| { |
| static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; |
| else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; |
| else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; |
| else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; |
| else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; |
| else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; |
| else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; |
| else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; |
| else alias ModifyTypePreservingTQ = Modifier!T; |
| } |
| @safe unittest |
| { |
| alias Intify(T) = int; |
| static assert(is(ModifyTypePreservingTQ!(Intify, real) == int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, const real) == const int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, inout real) == inout int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, inout const real) == inout const int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, shared real) == shared int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, shared const real) == shared const int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, shared inout real) == shared inout int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, shared inout const real) == shared inout const int)); |
| static assert(is(ModifyTypePreservingTQ!(Intify, immutable real) == immutable int)); |
| } |
| |
| // Substitute all `inout` qualifiers that appears in T to `const` |
| template substInout(T) |
| { |
| static if (is(T == immutable)) |
| { |
| alias substInout = T; |
| } |
| else static if (is(T : shared const U, U) || is(T : const U, U)) |
| { |
| // U is top-unqualified |
| mixin("alias substInout = " |
| ~ (is(T == shared) ? "shared " : "") |
| ~ (is(T == const) || is(T == inout) ? "const " : "") // substitute inout to const |
| ~ "substInoutForm!U;"); |
| } |
| else |
| static assert(0); |
| } |
| |
| private template substInoutForm(T) |
| { |
| static if (is(T == struct) || is(T == class) || is(T == union) || is(T == interface)) |
| { |
| alias substInoutForm = T; // prevent matching to the form of alias-this-ed type |
| } |
| else static if (is(T : V[K], K, V)) alias substInoutForm = substInout!V[substInout!K]; |
| else static if (is(T : U[n], U, size_t n)) alias substInoutForm = substInout!U[n]; |
| else static if (is(T : U[], U)) alias substInoutForm = substInout!U[]; |
| else static if (is(T : U*, U)) alias substInoutForm = substInout!U*; |
| else alias substInoutForm = T; |
| } |
| |
| /// used to declare an extern(D) function that is defined in a different module |
| template externDFunc(string fqn, T:FT*, FT) if (is(FT == function)) |
| { |
| static if (is(FT RT == return) && is(FT Args == function)) |
| { |
| import core.demangle : mangleFunc; |
| enum decl = { |
| string s = "extern(D) RT externDFunc(Args)"; |
| foreach (attr; __traits(getFunctionAttributes, FT)) |
| s ~= " " ~ attr; |
| return s ~ ";"; |
| }(); |
| pragma(mangle, mangleFunc!T(fqn)) mixin(decl); |
| } |
| else |
| static assert(0); |
| } |
| |
| template staticIota(int beg, int end) |
| { |
| static if (beg + 1 >= end) |
| { |
| static if (beg >= end) |
| { |
| alias staticIota = AliasSeq!(); |
| } |
| else |
| { |
| alias staticIota = AliasSeq!(+beg); |
| } |
| } |
| else |
| { |
| enum mid = beg + (end - beg) / 2; |
| alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end)); |
| } |
| } |
| |
| private struct __InoutWorkaroundStruct {} |
| @property T rvalueOf(T)(T val) { return val; } |
| @property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); |
| @property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); |
| |
| // taken from std.traits.isAssignable |
| template isAssignable(Lhs, Rhs = Lhs) |
| { |
| enum isAssignable = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs) && __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs); |
| } |
| |
| // taken from std.traits.isInnerClass |
| template isInnerClass(T) if (is(T == class)) |
| { |
| static if (is(typeof(T.outer))) |
| { |
| template hasOuterMember(T...) |
| { |
| static if (T.length == 0) |
| enum hasOuterMember = false; |
| else |
| enum hasOuterMember = T[0] == "outer" || hasOuterMember!(T[1 .. $]); |
| } |
| enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && !hasOuterMember!(__traits(allMembers, T)); |
| } |
| else |
| enum isInnerClass = false; |
| } |
| |
| template dtorIsNothrow(T) |
| { |
| enum dtorIsNothrow = is(typeof(function{T t=void;}) : void function() nothrow); |
| } |
| |
| // taken from std.meta.allSatisfy |
| template allSatisfy(alias F, T...) |
| { |
| static foreach (Ti; T) |
| { |
| static if (!is(typeof(allSatisfy) == bool) && // not yet defined |
| !F!(Ti)) |
| { |
| enum allSatisfy = false; |
| } |
| } |
| static if (!is(typeof(allSatisfy) == bool)) // if not yet defined |
| { |
| enum allSatisfy = true; |
| } |
| } |
| |
| // taken from std.meta.anySatisfy |
| template anySatisfy(alias F, Ts...) |
| { |
| static foreach (T; Ts) |
| { |
| static if (!is(typeof(anySatisfy) == bool) && // not yet defined |
| F!T) |
| { |
| enum anySatisfy = true; |
| } |
| } |
| static if (!is(typeof(anySatisfy) == bool)) // if not yet defined |
| { |
| enum anySatisfy = false; |
| } |
| } |
| |
| // simplified from std.traits.maxAlignment |
| template maxAlignment(Ts...) |
| if (Ts.length > 0) |
| { |
| enum maxAlignment = |
| { |
| size_t result = 0; |
| static foreach (T; Ts) |
| if (T.alignof > result) result = T.alignof; |
| return result; |
| }(); |
| } |
| |
| template classInstanceAlignment(T) |
| if (is(T == class)) |
| { |
| enum classInstanceAlignment = __traits(classInstanceAlignment, T); |
| } |
| |
| /// See $(REF hasElaborateMove, std,traits) |
| template hasElaborateMove(S) |
| { |
| static if (__traits(isStaticArray, S)) |
| { |
| enum bool hasElaborateMove = S.sizeof && hasElaborateMove!(BaseElemOf!S); |
| } |
| else static if (is(S == struct)) |
| { |
| enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!S))) && |
| !is(typeof(S.init.opPostMove(rvalueOf!S)))) || |
| anySatisfy!(.hasElaborateMove, Fields!S); |
| } |
| else |
| { |
| enum bool hasElaborateMove = false; |
| } |
| } |
| |
| // std.traits.hasElaborateDestructor |
| template hasElaborateDestructor(S) |
| { |
| static if (__traits(isStaticArray, S)) |
| { |
| enum bool hasElaborateDestructor = S.sizeof && hasElaborateDestructor!(BaseElemOf!S); |
| } |
| else static if (is(S == struct)) |
| { |
| // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then |
| // this should be the implementation, but until that's fixed, we need the |
| // uncommented code. |
| // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor"); |
| |
| enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]); |
| } |
| else |
| { |
| enum bool hasElaborateDestructor = false; |
| } |
| } |
| |
| private bool hasDtor(string[] members) |
| { |
| foreach (name; members) |
| { |
| if (name == "__xdtor") |
| return true; |
| } |
| |
| return false; |
| } |
| |
| @safe unittest |
| { |
| static struct NoDestructor {} |
| static assert(!hasElaborateDestructor!NoDestructor); |
| static assert(!hasElaborateDestructor!(NoDestructor[42])); |
| static assert(!hasElaborateDestructor!(NoDestructor[0])); |
| static assert(!hasElaborateDestructor!(NoDestructor[])); |
| |
| static struct HasDestructor { ~this() {} } |
| static assert( hasElaborateDestructor!HasDestructor); |
| static assert( hasElaborateDestructor!(HasDestructor[42])); |
| static assert(!hasElaborateDestructor!(HasDestructor[0])); |
| static assert(!hasElaborateDestructor!(HasDestructor[])); |
| |
| static struct HasDestructor2 { HasDestructor s; } |
| static assert( hasElaborateDestructor!HasDestructor2); |
| static assert( hasElaborateDestructor!(HasDestructor2[42])); |
| static assert(!hasElaborateDestructor!(HasDestructor2[0])); |
| static assert(!hasElaborateDestructor!(HasDestructor2[])); |
| |
| static class HasFinalizer { ~this() {} } |
| static assert(!hasElaborateDestructor!HasFinalizer); |
| |
| static struct HasUnion { union { HasDestructor s; } } |
| static assert(!hasElaborateDestructor!HasUnion); |
| static assert(!hasElaborateDestructor!(HasUnion[42])); |
| static assert(!hasElaborateDestructor!(HasUnion[0])); |
| static assert(!hasElaborateDestructor!(HasUnion[])); |
| |
| static assert(!hasElaborateDestructor!int); |
| static assert(!hasElaborateDestructor!(int[0])); |
| static assert(!hasElaborateDestructor!(int[42])); |
| static assert(!hasElaborateDestructor!(int[])); |
| } |
| |
| // https://issues.dlang.org/show_bug.cgi?id=24865 |
| @safe unittest |
| { |
| static struct S2 { ~this() {} } |
| static struct S3 { S2 field; } |
| static struct S6 { S3[0] field; } |
| |
| static assert( hasElaborateDestructor!S2); |
| static assert( hasElaborateDestructor!S3); |
| static assert(!hasElaborateDestructor!S6); |
| } |
| |
| // std.traits.hasElaborateCopyDestructor |
| template hasElaborateCopyConstructor(S) |
| { |
| static if (__traits(isStaticArray, S)) |
| { |
| enum bool hasElaborateCopyConstructor = S.sizeof && hasElaborateCopyConstructor!(BaseElemOf!S); |
| } |
| else static if (is(S == struct)) |
| { |
| enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, S) || __traits(hasPostblit, S); |
| } |
| else |
| { |
| enum bool hasElaborateCopyConstructor = false; |
| } |
| } |
| |
| @safe unittest |
| { |
| static struct S |
| { |
| int x; |
| this(return scope ref typeof(this) rhs) { } |
| this(int x, int y) {} |
| } |
| |
| static assert( hasElaborateCopyConstructor!S); |
| static assert(!hasElaborateCopyConstructor!(S[0][1])); |
| |
| static struct S2 |
| { |
| int x; |
| this(int x, int y) {} |
| } |
| |
| static assert(!hasElaborateCopyConstructor!S2); |
| |
| static struct S3 |
| { |
| int x; |
| this(return scope ref typeof(this) rhs, int x = 42) { } |
| this(int x, int y) {} |
| } |
| |
| static assert( hasElaborateCopyConstructor!S3); |
| |
| static struct S4 { union { S s; } } |
| |
| static assert(!hasElaborateCopyConstructor!S4); |
| } |
| |
| template hasElaborateAssign(S) |
| { |
| static if (__traits(isStaticArray, S)) |
| { |
| enum bool hasElaborateAssign = S.sizeof && hasElaborateAssign!(BaseElemOf!S); |
| } |
| else static if (is(S == struct)) |
| { |
| enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || |
| is(typeof(S.init.opAssign(lvalueOf!S))); |
| } |
| else |
| { |
| enum bool hasElaborateAssign = false; |
| } |
| } |
| |
| unittest |
| { |
| { |
| static struct S {} |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct S { int i; } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct S { void opAssign(S) {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct S { void opAssign(ref S) {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct S { void opAssign(int) {} } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct S { this(this) {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| // https://issues.dlang.org/show_bug.cgi?id=24834 |
| /+ |
| { |
| static struct S { this(ref S) {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| +/ |
| { |
| static struct S { ~this() {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct S { @disable void opAssign(S); } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member {} |
| static struct S { Member member; } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member { void opAssign(Member) {} } |
| static struct S { Member member; } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member {} |
| static struct S { Member member; void opAssign(S) {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member { @disable void opAssign(Member); } |
| static struct S { Member member; } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member { @disable void opAssign(Member); } |
| static struct S { Member member; void opAssign(S) {} } |
| static assert( hasElaborateAssign!S); |
| static assert( hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member { void opAssign(Member) {} } |
| static struct S { Member member; @disable void opAssign(S); } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| { |
| static struct Member { void opAssign(Member) {} } |
| static struct S { union { Member member; } } |
| static assert(!hasElaborateAssign!S); |
| static assert(!hasElaborateAssign!(S[10])); |
| static assert(!hasElaborateAssign!(S[0])); |
| static assert(!hasElaborateAssign!(S[])); |
| } |
| |
| static assert(!hasElaborateAssign!int); |
| static assert(!hasElaborateAssign!(string[])); |
| static assert(!hasElaborateAssign!Object); |
| } |
| |
| template hasIndirections(T) |
| { |
| static if (is(T == enum)) |
| enum hasIndirections = hasIndirections!(OriginalType!T); |
| else static if (is(T == struct) || is(T == union)) |
| enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof)); |
| else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface)) |
| enum hasIndirections = true; |
| else static if (is(T == E[N], E, size_t N)) |
| enum hasIndirections = T.sizeof && (is(immutable E == immutable void) || hasIndirections!(BaseElemOf!E)); |
| else static if (isFunctionPointer!T) |
| enum hasIndirections = false; |
| else |
| enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T; |
| } |
| |
| @safe unittest |
| { |
| static assert(!hasIndirections!int); |
| static assert(!hasIndirections!(const int)); |
| |
| static assert( hasIndirections!(int*)); |
| static assert( hasIndirections!(const int*)); |
| |
| static assert( hasIndirections!(int[])); |
| static assert(!hasIndirections!(int[42])); |
| static assert(!hasIndirections!(int[0])); |
| |
| static assert( hasIndirections!(int*)); |
| static assert( hasIndirections!(int*[])); |
| static assert( hasIndirections!(int*[42])); |
| static assert(!hasIndirections!(int*[0])); |
| |
| static assert( hasIndirections!string); |
| static assert( hasIndirections!(string[])); |
| static assert( hasIndirections!(string[42])); |
| static assert(!hasIndirections!(string[0])); |
| |
| static assert( hasIndirections!(void[])); |
| static assert( hasIndirections!(void[17])); |
| static assert(!hasIndirections!(void[0])); |
| |
| static assert( hasIndirections!(string[int])); |
| static assert( hasIndirections!(string[int]*)); |
| static assert( hasIndirections!(string[int][])); |
| static assert( hasIndirections!(string[int][12])); |
| static assert(!hasIndirections!(string[int][0])); |
| |
| static assert(!hasIndirections!(int function(string))); |
| static assert( hasIndirections!(int delegate(string))); |
| static assert(!hasIndirections!(const(int function(string)))); |
| static assert( hasIndirections!(const(int delegate(string)))); |
| static assert(!hasIndirections!(immutable(int function(string)))); |
| static assert( hasIndirections!(immutable(int delegate(string)))); |
| |
| static class C {} |
| static assert( hasIndirections!C); |
| |
| static interface I {} |
| static assert( hasIndirections!I); |
| |
| { |
| enum E : int { a } |
| static assert(!hasIndirections!E); |
| } |
| { |
| enum E : int* { a } |
| static assert( hasIndirections!E); |
| } |
| { |
| enum E : string { a = "" } |
| static assert( hasIndirections!E); |
| } |
| { |
| enum E : int[] { a = null } |
| static assert( hasIndirections!E); |
| } |
| { |
| enum E : int[3] { a = [1, 2, 3] } |
| static assert(!hasIndirections!E); |
| } |
| { |
| enum E : int*[3] { a = [null, null, null] } |
| static assert( hasIndirections!E); |
| } |
| { |
| enum E : int*[0] { a = int*[0].init } |
| static assert(!hasIndirections!E); |
| } |
| { |
| enum E : C { a = null } |
| static assert( hasIndirections!E); |
| } |
| { |
| enum E : I { a = null } |
| static assert( hasIndirections!E); |
| } |
| |
| { |
| static struct S {} |
| static assert(!hasIndirections!S); |
| |
| enum E : S { a = S.init } |
| static assert(!hasIndirections!S); |
| } |
| { |
| static struct S { int i; } |
| static assert(!hasIndirections!S); |
| |
| enum E : S { a = S.init } |
| static assert(!hasIndirections!S); |
| } |
| { |
| static struct S { C c; } |
| static assert( hasIndirections!S); |
| |
| enum E : S { a = S.init } |
| static assert( hasIndirections!S); |
| } |
| { |
| static struct S { int[] arr; } |
| static assert( hasIndirections!S); |
| |
| enum E : S { a = S.init } |
| static assert( hasIndirections!S); |
| } |
| { |
| int local; |
| struct S { void foo() { ++local; } } |
| static assert( hasIndirections!S); |
| |
| enum E : S { a = S.init } |
| static assert( hasIndirections!S); |
| } |
| |
| { |
| static union U {} |
| static assert(!hasIndirections!U); |
| } |
| { |
| static union U { int i; } |
| static assert(!hasIndirections!U); |
| } |
| { |
| static union U { C c; } |
| static assert( hasIndirections!U); |
| } |
| { |
| static union U { int[] arr; } |
| static assert( hasIndirections!U); |
| } |
| } |
| |
| // https://issues.dlang.org/show_bug.cgi?id=12000 |
| @safe unittest |
| { |
| static struct S(T) |
| { |
| static assert(hasIndirections!T); |
| } |
| |
| static class A(T) |
| { |
| S!A a; |
| } |
| |
| A!int dummy; |
| } |
| |
| // https://github.com/dlang/dmd/issues/20812 |
| @safe unittest |
| { |
| static assert(!hasIndirections!void); |
| static assert(!hasIndirections!(const void)); |
| static assert(!hasIndirections!(inout void)); |
| static assert(!hasIndirections!(immutable void)); |
| static assert(!hasIndirections!(shared void)); |
| |
| static assert( hasIndirections!(void*)); |
| static assert( hasIndirections!(const void*)); |
| static assert( hasIndirections!(inout void*)); |
| static assert( hasIndirections!(immutable void*)); |
| static assert( hasIndirections!(shared void*)); |
| |
| static assert( hasIndirections!(void[])); |
| static assert( hasIndirections!(const void[])); |
| static assert( hasIndirections!(inout void[])); |
| static assert( hasIndirections!(immutable void[])); |
| static assert( hasIndirections!(shared void[])); |
| |
| static assert( hasIndirections!(void[42])); |
| static assert( hasIndirections!(const void[42])); |
| static assert( hasIndirections!(inout void[42])); |
| static assert( hasIndirections!(immutable void[42])); |
| static assert( hasIndirections!(shared void[42])); |
| |
| static assert(!hasIndirections!(void[0])); |
| static assert(!hasIndirections!(const void[0])); |
| static assert(!hasIndirections!(inout void[0])); |
| static assert(!hasIndirections!(immutable void[0])); |
| static assert(!hasIndirections!(shared void[0])); |
| } |
| |
| template hasUnsharedIndirections(T) |
| { |
| static if (is(T == immutable)) |
| enum hasUnsharedIndirections = false; |
| else static if (is(T == struct) || is(T == union)) |
| enum hasUnsharedIndirections = anySatisfy!(.hasUnsharedIndirections, Fields!T); |
| else static if (is(T : E[N], E, size_t N)) |
| enum hasUnsharedIndirections = is(E == void) ? false : hasUnsharedIndirections!E; |
| else static if (isFunctionPointer!T) |
| enum hasUnsharedIndirections = false; |
| else static if (isPointer!T) |
| enum hasUnsharedIndirections = !is(T : shared(U)*, U) && !is(T : immutable(U)*, U); |
| else static if (isDynamicArray!T) |
| enum hasUnsharedIndirections = !is(T : shared(V)[], V) && !is(T : immutable(V)[], V); |
| else static if (is(T == class) || is(T == interface)) |
| enum hasUnsharedIndirections = !is(T : shared(W), W); |
| else |
| enum hasUnsharedIndirections = isDelegate!T || __traits(isAssociativeArray, T); // TODO: how to handle these? |
| } |
| |
| unittest |
| { |
| static struct Foo { shared(int)* val; } |
| |
| static assert(!hasUnsharedIndirections!(immutable(char)*)); |
| static assert(!hasUnsharedIndirections!(string)); |
| |
| static assert(!hasUnsharedIndirections!(Foo)); |
| static assert( hasUnsharedIndirections!(Foo*)); |
| static assert(!hasUnsharedIndirections!(shared(Foo)*)); |
| static assert(!hasUnsharedIndirections!(immutable(Foo)*)); |
| |
| int local; |
| struct HasContextPointer { int opCall() { return ++local; } } |
| static assert(hasIndirections!HasContextPointer); |
| } |
| |
| enum bool isAggregateType(T) = is(T == struct) || is(T == union) || |
| is(T == class) || is(T == interface); |
| |
| enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T; |
| |
| enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; |
| |
| template OriginalType(T) |
| { |
| template Impl(T) |
| { |
| static if (is(T U == enum)) alias Impl = OriginalType!U; |
| else alias Impl = T; |
| } |
| |
| alias OriginalType = ModifyTypePreservingTQ!(Impl, T); |
| } |
| |
| template DynamicArrayTypeOf(T) |
| { |
| static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) |
| alias X = DynamicArrayTypeOf!AT; |
| else |
| alias X = OriginalType!T; |
| |
| static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; }))) |
| alias DynamicArrayTypeOf = X; |
| else |
| static assert(0, T.stringof ~ " is not a dynamic array"); |
| } |
| |
| private template AliasThisTypeOf(T) |
| if (isAggregateType!T) |
| { |
| alias members = __traits(getAliasThis, T); |
| |
| static if (members.length == 1) |
| alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0])); |
| else |
| static assert(0, T.stringof~" does not have alias this type"); |
| } |
| |
| template isFunctionPointer(T...) |
| if (T.length == 1) |
| { |
| static if (is(T[0] U) || is(typeof(T[0]) U)) |
| { |
| static if (is(U F : F*) && is(F == function)) |
| enum bool isFunctionPointer = true; |
| else |
| enum bool isFunctionPointer = false; |
| } |
| else |
| enum bool isFunctionPointer = false; |
| } |
| |
| template isDelegate(T...) |
| if (T.length == 1) |
| { |
| static if (is(typeof(& T[0]) U : U*) && is(typeof(& T[0]) U == delegate)) |
| { |
| // T is a (nested) function symbol. |
| enum bool isDelegate = true; |
| } |
| else static if (is(T[0] W) || is(typeof(T[0]) W)) |
| { |
| // T is an expression or a type. Take the type of it and examine. |
| enum bool isDelegate = is(W == delegate); |
| } |
| else |
| enum bool isDelegate = false; |
| } |
| |
| // std.meta.Filter |
| template Filter(alias pred, TList...) |
| { |
| static if (TList.length == 0) |
| { |
| alias Filter = AliasSeq!(); |
| } |
| else static if (TList.length == 1) |
| { |
| static if (pred!(TList[0])) |
| alias Filter = AliasSeq!(TList[0]); |
| else |
| alias Filter = AliasSeq!(); |
| } |
| /* The next case speeds up compilation by reducing |
| * the number of Filter instantiations |
| */ |
| else static if (TList.length == 2) |
| { |
| static if (pred!(TList[0])) |
| { |
| static if (pred!(TList[1])) |
| alias Filter = AliasSeq!(TList[0], TList[1]); |
| else |
| alias Filter = AliasSeq!(TList[0]); |
| } |
| else |
| { |
| static if (pred!(TList[1])) |
| alias Filter = AliasSeq!(TList[1]); |
| else |
| alias Filter = AliasSeq!(); |
| } |
| } |
| else |
| { |
| alias Filter = |
| AliasSeq!( |
| Filter!(pred, TList[ 0 .. $/2]), |
| Filter!(pred, TList[$/2 .. $ ])); |
| } |
| } |
| |
| // std.meta.staticMap |
| template staticMap(alias F, T...) |
| { |
| static if (T.length == 0) |
| { |
| alias staticMap = AliasSeq!(); |
| } |
| else static if (T.length == 1) |
| { |
| alias staticMap = AliasSeq!(F!(T[0])); |
| } |
| /* Cases 2 to 8 improve compile performance by reducing |
| * the number of recursive instantiations of staticMap |
| */ |
| else static if (T.length == 2) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1])); |
| } |
| else static if (T.length == 3) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2])); |
| } |
| else static if (T.length == 4) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3])); |
| } |
| else static if (T.length == 5) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4])); |
| } |
| else static if (T.length == 6) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5])); |
| } |
| else static if (T.length == 7) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6])); |
| } |
| else static if (T.length == 8) |
| { |
| alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6]), F!(T[7])); |
| } |
| else |
| { |
| alias staticMap = |
| AliasSeq!( |
| staticMap!(F, T[ 0 .. $/2]), |
| staticMap!(F, T[$/2 .. $ ])); |
| } |
| } |
| |
| // std.exception.assertCTFEable |
| version (CoreUnittest) package(core) |
| void assertCTFEable(alias dg)() |
| { |
| static assert({ cast(void) dg(); return true; }()); |
| cast(void) dg(); |
| } |
| |
| // std.traits.FunctionTypeOf |
| /* |
| Get the function type from a callable object `func`. |
| |
| Using builtin `typeof` on a property function yields the types of the |
| property value, not of the property function itself. Still, |
| `FunctionTypeOf` is able to obtain function types of properties. |
| |
| Note: |
| Do not confuse function types with function pointer types; function types are |
| usually used for compile-time reflection purposes. |
| */ |
| template FunctionTypeOf(func...) |
| if (func.length == 1 /*&& isCallable!func*/) |
| { |
| static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate)) |
| { |
| alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol |
| } |
| else static if (is(typeof(& func[0].opCall) Fobj == delegate)) |
| { |
| alias FunctionTypeOf = Fobj; // HIT: callable object |
| } |
| else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) |
| { |
| alias FunctionTypeOf = Ftyp; // HIT: callable type |
| } |
| else static if (is(func[0] T) || is(typeof(func[0]) T)) |
| { |
| static if (is(T == function)) |
| alias FunctionTypeOf = T; // HIT: function |
| else static if (is(T Fptr : Fptr*) && is(Fptr == function)) |
| alias FunctionTypeOf = Fptr; // HIT: function pointer |
| else static if (is(T Fdlg == delegate)) |
| alias FunctionTypeOf = Fdlg; // HIT: delegate |
| else |
| static assert(0); |
| } |
| else |
| static assert(0); |
| } |
| |
| @safe unittest |
| { |
| class C |
| { |
| int value() @property { return 0; } |
| } |
| static assert(is( typeof(C.value) == int )); |
| static assert(is( FunctionTypeOf!(C.value) == function )); |
| } |
| |
| @system unittest |
| { |
| int test(int a); |
| int propGet() @property; |
| int propSet(int a) @property; |
| int function(int) test_fp; |
| int delegate(int) test_dg; |
| static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) )); |
| static assert(is( typeof(test) == FunctionTypeOf!test )); |
| static assert(is( typeof(test) == FunctionTypeOf!test_fp )); |
| static assert(is( typeof(test) == FunctionTypeOf!test_dg )); |
| alias int GetterType() @property; |
| alias int SetterType(int) @property; |
| static assert(is( FunctionTypeOf!propGet == GetterType )); |
| static assert(is( FunctionTypeOf!propSet == SetterType )); |
| |
| interface Prop { int prop() @property; } |
| Prop prop; |
| static assert(is( FunctionTypeOf!(Prop.prop) == GetterType )); |
| static assert(is( FunctionTypeOf!(prop.prop) == GetterType )); |
| |
| class Callable { int opCall(int) { return 0; } } |
| auto call = new Callable; |
| static assert(is( FunctionTypeOf!call == typeof(test) )); |
| |
| struct StaticCallable { static int opCall(int) { return 0; } } |
| StaticCallable stcall_val; |
| StaticCallable* stcall_ptr; |
| static assert(is( FunctionTypeOf!stcall_val == typeof(test) )); |
| static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) )); |
| |
| interface Overloads |
| { |
| void test(string); |
| real test(real); |
| int test(int); |
| int test() @property; |
| } |
| alias ov = __traits(getVirtualMethods, Overloads, "test"); |
| alias F_ov0 = FunctionTypeOf!(ov[0]); |
| alias F_ov1 = FunctionTypeOf!(ov[1]); |
| alias F_ov2 = FunctionTypeOf!(ov[2]); |
| alias F_ov3 = FunctionTypeOf!(ov[3]); |
| static assert(is(F_ov0* == void function(string))); |
| static assert(is(F_ov1* == real function(real))); |
| static assert(is(F_ov2* == int function(int))); |
| static assert(is(F_ov3* == int function() @property)); |
| |
| alias F_dglit = FunctionTypeOf!((int a){ return a; }); |
| static assert(is(F_dglit* : int function(int))); |
| } |
| |
| // std.traits.ReturnType |
| /* |
| Get the type of the return value from a function, |
| a pointer to function, a delegate, a struct |
| with an opCall, a pointer to a struct with an opCall, |
| or a class with an `opCall`. Please note that $(D_KEYWORD ref) |
| is not part of a type, but the attribute of the function |
| (see template $(LREF functionAttributes)). |
| */ |
| template ReturnType(func...) |
| if (func.length == 1 /*&& isCallable!func*/) |
| { |
| static if (is(FunctionTypeOf!func R == return)) |
| alias ReturnType = R; |
| else |
| static assert(0, "argument has no return type"); |
| } |
| |
| // |
| @safe unittest |
| { |
| int foo(); |
| ReturnType!foo x; // x is declared as int |
| } |
| |
| @safe unittest |
| { |
| struct G |
| { |
| int opCall (int i) { return 1;} |
| } |
| |
| alias ShouldBeInt = ReturnType!G; |
| static assert(is(ShouldBeInt == int)); |
| |
| G g; |
| static assert(is(ReturnType!g == int)); |
| |
| G* p; |
| alias pg = ReturnType!p; |
| static assert(is(pg == int)); |
| |
| class C |
| { |
| int opCall (int i) { return 1;} |
| } |
| |
| static assert(is(ReturnType!C == int)); |
| |
| C c; |
| static assert(is(ReturnType!c == int)); |
| |
| class Test |
| { |
| int prop() @property { return 0; } |
| } |
| alias R_Test_prop = ReturnType!(Test.prop); |
| static assert(is(R_Test_prop == int)); |
| |
| alias R_dglit = ReturnType!((int a) { return a; }); |
| static assert(is(R_dglit == int)); |
| } |
| |
| // std.traits.Parameters |
| /* |
| Get, as a tuple, the types of the parameters to a function, a pointer |
| to function, a delegate, a struct with an `opCall`, a pointer to a |
| struct with an `opCall`, or a class with an `opCall`. |
| */ |
| template Parameters(func...) |
| if (func.length == 1 /*&& isCallable!func*/) |
| { |
| static if (is(FunctionTypeOf!func P == function)) |
| alias Parameters = P; |
| else |
| static assert(0, "argument has no parameters"); |
| } |
| |
| // |
| @safe unittest |
| { |
| int foo(int, long); |
| void bar(Parameters!foo); // declares void bar(int, long); |
| void abc(Parameters!foo[1]); // declares void abc(long); |
| } |
| |
| @safe unittest |
| { |
| int foo(int i, bool b) { return 0; } |
| static assert(is(Parameters!foo == AliasSeq!(int, bool))); |
| static assert(is(Parameters!(typeof(&foo)) == AliasSeq!(int, bool))); |
| |
| struct S { real opCall(real r, int i) { return 0.0; } } |
| S s; |
| static assert(is(Parameters!S == AliasSeq!(real, int))); |
| static assert(is(Parameters!(S*) == AliasSeq!(real, int))); |
| static assert(is(Parameters!s == AliasSeq!(real, int))); |
| |
| class Test |
| { |
| int prop() @property { return 0; } |
| } |
| alias P_Test_prop = Parameters!(Test.prop); |
| static assert(P_Test_prop.length == 0); |
| |
| alias P_dglit = Parameters!((int a){}); |
| static assert(P_dglit.length == 1); |
| static assert(is(P_dglit[0] == int)); |
| } |
| |
| // Return `true` if `Type` has `member` that evaluates to `true` in a static if condition |
| enum isTrue(Type, string member) = __traits(compiles, { static if (__traits(getMember, Type, member)) {} else static assert(0); }); |
| |
| unittest |
| { |
| static struct T |
| { |
| enum a = true; |
| enum b = false; |
| enum c = 1; |
| enum d = 45; |
| enum e = "true"; |
| enum f = ""; |
| enum g = null; |
| alias h = bool; |
| } |
| |
| static assert( isTrue!(T, "a")); |
| static assert(!isTrue!(T, "b")); |
| static assert( isTrue!(T, "c")); |
| static assert( isTrue!(T, "d")); |
| static assert( isTrue!(T, "e")); |
| static assert( isTrue!(T, "f")); |
| static assert(!isTrue!(T, "g")); |
| static assert(!isTrue!(T, "h")); |
| } |
| |
| template hasUDA(alias symbol, alias attribute) |
| { |
| enum isAttr(T) = is(T == attribute); |
| |
| enum hasUDA = anySatisfy!(isAttr, __traits(getAttributes, symbol)); |
| } |
| |
| unittest |
| { |
| enum SomeUDA; |
| |
| struct Test |
| { |
| int woUDA; |
| @SomeUDA int oneUDA; |
| @SomeUDA @SomeUDA int twoUDAs; |
| } |
| |
| static assert(hasUDA!(Test.oneUDA, SomeUDA)); |
| static assert(hasUDA!(Test.twoUDAs, SomeUDA)); |
| static assert(!hasUDA!(Test.woUDA, SomeUDA)); |
| } |