diff options
Diffstat (limited to 'libphobos/src/std/traits.d')
-rw-r--r-- | libphobos/src/std/traits.d | 3180 |
1 files changed, 2130 insertions, 1050 deletions
diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index 7badab42..230a7c6 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -8,12 +8,12 @@ * $(DIVC quickindex, * $(BOOKTABLE , * $(TR $(TH Category) $(TH Templates)) - * $(TR $(TD Symbol Name _traits) $(TD + * $(TR $(TD Symbol Name traits) $(TD * $(LREF fullyQualifiedName) * $(LREF moduleName) * $(LREF packageName) * )) - * $(TR $(TD Function _traits) $(TD + * $(TR $(TD Function traits) $(TD * $(LREF isFunction) * $(LREF arity) * $(LREF functionAttributes) @@ -31,7 +31,7 @@ * $(LREF SetFunctionAttributes) * $(LREF variadicFunctionStyle) * )) - * $(TR $(TD Aggregate Type _traits) $(TD + * $(TR $(TD Aggregate Type traits) $(TD * $(LREF BaseClassesTuple) * $(LREF BaseTypeTuple) * $(LREF classInstanceAlignment) @@ -42,6 +42,7 @@ * $(LREF hasElaborateAssign) * $(LREF hasElaborateCopyConstructor) * $(LREF hasElaborateDestructor) + * $(LREF hasElaborateMove) * $(LREF hasIndirections) * $(LREF hasMember) * $(LREF hasStaticMember) @@ -58,6 +59,7 @@ * )) * $(TR $(TD Type Conversion) $(TD * $(LREF CommonType) + * $(LREF AllImplicitConversionTargets) * $(LREF ImplicitConversionTargets) * $(LREF CopyTypeQualifiers) * $(LREF CopyConstness) @@ -73,6 +75,7 @@ * $(LREF SharedOf) * $(LREF SharedInoutOf) * $(LREF SharedConstOf) + * $(LREF SharedConstInoutOf) * $(LREF ImmutableOf) * $(LREF QualifierOf) * )) @@ -128,6 +131,7 @@ * $(LREF OriginalType) * $(LREF PointerTarget) * $(LREF Signed) + * $(LREF Unconst) * $(LREF Unqual) * $(LREF Unsigned) * $(LREF ValueType) @@ -146,27 +150,26 @@ * ) * ) * - * Copyright: Copyright Digital Mars 2005 - 2009. + * Copyright: Copyright The D Language Foundation 2005 - 2009. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: $(HTTP digitalmars.com, Walter Bright), - * Tomasz Stachowiak ($(D isExpressions)), + * Tomasz Stachowiak (`isExpressions`), * $(HTTP erdani.org, Andrei Alexandrescu), * Shin Fujishiro, * $(HTTP octarineparrot.com, Robert Clipsham), * $(HTTP klickverbot.at, David Nadlinger), * Kenji Hara, * Shoichi Kato - * Source: $(PHOBOSSRC std/_traits.d) + * Source: $(PHOBOSSRC std/traits.d) */ -/* Copyright Digital Mars 2005 - 2009. +/* Copyright The D Language Foundation 2005 - 2009. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ module std.traits; -import std.meta : AliasSeq, allSatisfy; -import std.functional : unaryFun; +import std.meta : AliasSeq, allSatisfy, anySatisfy, ApplyLeft; // Legacy inheritance from std.typetuple // See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797 @@ -176,88 +179,11 @@ import std.meta : staticMapMeta = staticMap; alias staticMap = staticMapMeta; /////////////////////////////////////////////////////////////////////////////// -// Functions +// Type lists /////////////////////////////////////////////////////////////////////////////// -// Petit demangler -// (this or similar thing will eventually go to std.demangle if necessary -// ctfe stuffs are available) private { - struct Demangle(T) - { - T value; // extracted information - string rest; - } - - /* Demangles mstr as the storage class part of Argument. */ - Demangle!uint demangleParameterStorageClass(string mstr) - { - uint pstc = 0; // parameter storage class - - // Argument --> Argument2 | M Argument2 - if (mstr.length > 0 && mstr[0] == 'M') - { - pstc |= ParameterStorageClass.scope_; - mstr = mstr[1 .. $]; - } - - // Argument2 --> Type | J Type | K Type | L Type - ParameterStorageClass stc2; - - switch (mstr.length ? mstr[0] : char.init) - { - case 'J': stc2 = ParameterStorageClass.out_; break; - case 'K': stc2 = ParameterStorageClass.ref_; break; - case 'L': stc2 = ParameterStorageClass.lazy_; break; - case 'N': if (mstr.length >= 2 && mstr[1] == 'k') - stc2 = ParameterStorageClass.return_; - break; - default : break; - } - if (stc2 != ParameterStorageClass.init) - { - pstc |= stc2; - mstr = mstr[1 .. $]; - if (stc2 & ParameterStorageClass.return_) - mstr = mstr[1 .. $]; - } - - return Demangle!uint(pstc, mstr); - } - - /* Demangles mstr as FuncAttrs. */ - Demangle!uint demangleFunctionAttributes(string mstr) - { - immutable LOOKUP_ATTRIBUTE = - [ - 'a': FunctionAttribute.pure_, - 'b': FunctionAttribute.nothrow_, - 'c': FunctionAttribute.ref_, - 'd': FunctionAttribute.property, - 'e': FunctionAttribute.trusted, - 'f': FunctionAttribute.safe, - 'i': FunctionAttribute.nogc, - 'j': FunctionAttribute.return_, - 'l': FunctionAttribute.scope_ - ]; - uint atts = 0; - - // FuncAttrs --> FuncAttr | FuncAttr FuncAttrs - // FuncAttr --> empty | Na | Nb | Nc | Nd | Ne | Nf | Ni | Nj - // except 'Ng' == inout, because it is a qualifier of function type - while (mstr.length >= 2 && mstr[0] == 'N' && mstr[1] != 'g' && mstr[1] != 'k') - { - if (FunctionAttribute att = LOOKUP_ATTRIBUTE[ mstr[1] ]) - { - atts |= att; - mstr = mstr[2 .. $]; - } - else assert(0); - } - return Demangle!uint(atts, mstr); - } - static if (is(ucent)) { alias CentTypeList = AliasSeq!(cent, ucent); @@ -281,28 +207,131 @@ private alias CharTypeList = AliasSeq!(char, wchar, dchar); } -package +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `inout` qualifier added. + */ +alias InoutOf(T) = inout(T); + +/// +@safe unittest { - // Add the mutable qualifier to the given type T. - template MutableOf(T) { alias MutableOf = T ; } + static assert(is(InoutOf!(int) == inout int)); + static assert(is(InoutOf!(inout int) == inout int)); + static assert(is(InoutOf!(const int) == inout const int)); + static assert(is(InoutOf!(shared int) == inout shared int)); } -/// Add the inout qualifier to the given type T. -template InoutOf(T) { alias InoutOf = inout(T) ; } -/// Add the const qualifier to the given type T. -template ConstOf(T) { alias ConstOf = const(T) ; } -/// Add the shared qualifier to the given type T. -template SharedOf(T) { alias SharedOf = shared(T) ; } -/// Add the shared and inout qualifiers to the given type T. -template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); } -/// Add the shared and const qualifiers to the given type T. -template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); } -/// Add the immutable qualifier to the given type T. -template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; } +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `const` qualifier added. + */ +alias ConstOf(T) = const(T); + +/// +@safe unittest +{ + static assert(is(ConstOf!(int) == const int)); + static assert(is(ConstOf!(const int) == const int)); + static assert(is(ConstOf!(inout int) == const inout int)); + static assert(is(ConstOf!(shared int) == const shared int)); +} + +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `shared` qualifier added. + */ +alias SharedOf(T) = shared(T); + +/// +@safe unittest +{ + static assert(is(SharedOf!(int) == shared int)); + static assert(is(SharedOf!(shared int) == shared int)); + static assert(is(SharedOf!(inout int) == shared inout int)); + static assert(is(SharedOf!(immutable int) == shared immutable int)); +} + +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `inout` and `shared` qualifiers added. + */ +alias SharedInoutOf(T) = shared(inout(T)); + +/// +@safe unittest +{ + static assert(is(SharedInoutOf!(int) == shared inout int)); + static assert(is(SharedInoutOf!(int) == inout shared int)); + + static assert(is(SharedInoutOf!(const int) == shared inout const int)); + static assert(is(SharedInoutOf!(immutable int) == shared inout immutable int)); +} + +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `const` and `shared` qualifiers added. + */ +alias SharedConstOf(T) = shared(const(T)); + +/// +@safe unittest +{ + static assert(is(SharedConstOf!(int) == shared const int)); + static assert(is(SharedConstOf!(int) == const shared int)); + + static assert(is(SharedConstOf!(inout int) == shared inout const int)); + // immutable variables are implicitly shared and const + static assert(is(SharedConstOf!(immutable int) == immutable int)); +} + +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `const`, `shared`, and `inout` qualifiers added. + */ +alias SharedConstInoutOf(T) = shared(const(inout(T))); + +/// +@safe unittest +{ + static assert(is(SharedConstInoutOf!(int) == shared const inout int)); + static assert(is(SharedConstInoutOf!(int) == const shared inout int)); + static assert(is(SharedConstInoutOf!(inout int) == shared inout const int)); + // immutable variables are implicitly shared and const + static assert(is(SharedConstInoutOf!(immutable int) == immutable int)); +} + +/** + * Params: + * T = The type to qualify + * Returns: + * `T` with the `immutable` qualifier added. + */ +alias ImmutableOf(T) = immutable(T); + +/// +@safe unittest +{ + static assert(is(ImmutableOf!(int) == immutable int)); + static assert(is(ImmutableOf!(const int) == immutable int)); + static assert(is(ImmutableOf!(inout int) == immutable int)); + static assert(is(ImmutableOf!(shared int) == immutable int)); +} @safe unittest { - static assert(is( MutableOf!int == int)); static assert(is( InoutOf!int == inout int)); static assert(is( ConstOf!int == const int)); static assert(is( SharedOf!int == shared int)); @@ -311,16 +340,45 @@ template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; } static assert(is( ImmutableOf!int == immutable int)); } -/// Get qualifier template from the given type T +/** + * Gives a template that can be used to apply the same + * attributes that are on the given type `T`. E.g. passing + * `inout shared int` will return `SharedInoutOf`. + * + * Params: + * T = the type to check qualifiers from + * Returns: + * The qualifier template from the given type `T` + */ template QualifierOf(T) { - static if (is(T == shared(const U), U)) alias QualifierOf = SharedConstOf; - else static if (is(T == const U , U)) alias QualifierOf = ConstOf; - else static if (is(T == shared(inout U), U)) alias QualifierOf = SharedInoutOf; - else static if (is(T == inout U , U)) alias QualifierOf = InoutOf; - else static if (is(T == immutable U , U)) alias QualifierOf = ImmutableOf; - else static if (is(T == shared U , U)) alias QualifierOf = SharedOf; - else alias QualifierOf = MutableOf; + static if (is(immutable T == T)) + { + alias QualifierOf = ImmutableOf; + } + else + { + private enum quals = is(const T == T) | (is(inout T == T) << 1) | (is(shared T == T) << 2); + static if (quals == 0) { import std.meta : Alias; alias QualifierOf = Alias; } + else static if (quals == 1) alias QualifierOf = ConstOf; + else static if (quals == 2) alias QualifierOf = InoutOf; + else static if (quals == 3) alias QualifierOf = ConstInoutOf; + else static if (quals == 4) alias QualifierOf = SharedOf; + else static if (quals == 5) alias QualifierOf = SharedConstOf; + else static if (quals == 6) alias QualifierOf = SharedInoutOf; + else alias QualifierOf = SharedConstInoutOf; + } +} + +/// +@safe unittest +{ + static assert(__traits(isSame, QualifierOf!(shared const inout int), SharedConstInoutOf)); + static assert(__traits(isSame, QualifierOf!(immutable int), ImmutableOf)); + static assert(__traits(isSame, QualifierOf!(shared int), SharedOf)); + static assert(__traits(isSame, QualifierOf!(shared inout int), SharedInoutOf)); + import std.meta : Alias; + static assert(__traits(isSame, QualifierOf!(int), Alias)); } @safe unittest @@ -334,9 +392,10 @@ template QualifierOf(T) alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long)); } -version (unittest) +version (StdUnittest) { - alias TypeQualifierList = AliasSeq!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf); + import std.meta : Alias; + alias TypeQualifierList = AliasSeq!(Alias, ConstOf, SharedOf, SharedConstOf, ImmutableOf); struct SubTypeOf(T) { @@ -355,12 +414,14 @@ template packageName(alias T) { import std.algorithm.searching : startsWith; + enum bool isNotFunc = !isSomeFunction!(T); + static if (__traits(compiles, parentOf!T)) enum parent = packageName!(parentOf!T); else enum string parent = null; - static if (T.stringof.startsWith("package ")) + static if (isNotFunc && T.stringof.startsWith("package ")) enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $]; else static if (parent) enum packageName = parent; @@ -371,7 +432,6 @@ template packageName(alias T) /// @safe unittest { - import std.traits; static assert(packageName!packageName == "std"); } @@ -379,8 +439,7 @@ template packageName(alias T) { import std.array; - // Commented out because of dmd @@@BUG8922@@@ - // static assert(packageName!std == "std"); // this package (currently: "std.std") + static assert(packageName!std == "std"); static assert(packageName!(std.traits) == "std"); // this module static assert(packageName!packageName == "std"); // symbol in this module static assert(packageName!(std.array) == "std"); // other module from same package @@ -394,7 +453,7 @@ template packageName(alias T) static assert(packageName!(X12287!int.i) == "std"); } -version (none) version (unittest) //Please uncomment me when changing packageName to test global imports +version (none) @safe unittest //Please uncomment me when changing packageName to test global imports { import core.sync.barrier; // global import static assert(packageName!core == "core"); @@ -402,6 +461,25 @@ version (none) version (unittest) //Please uncomment me when changing packageNam static assert(packageName!Barrier == "core.sync"); } +/// +@safe unittest +{ + static assert(packageName!moduleName == "std"); +} + +// https://issues.dlang.org/show_bug.cgi?id=13741 +@safe unittest +{ + import std.ascii : isWhite; + static assert(packageName!(isWhite) == "std"); + + struct Foo{void opCall(int){}} + static assert(packageName!(Foo.opCall) == "std"); + + @property void function(int) vf; + static assert(packageName!(vf) == "std"); +} + /** * Get the module name (including package) for the given symbol. */ @@ -409,9 +487,13 @@ template moduleName(alias T) { import std.algorithm.searching : startsWith; - static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package"); + enum bool isNotFunc = !isSomeFunction!(T); - static if (T.stringof.startsWith("module ")) + static if (isNotFunc) + static assert(!T.stringof.startsWith("package "), + "cannot get the module name for a package"); + + static if (isNotFunc && T.stringof.startsWith("module ")) { static if (__traits(compiles, packageName!T)) enum packagePrefix = packageName!T ~ '.'; @@ -427,7 +509,6 @@ template moduleName(alias T) /// @safe unittest { - import std.traits; static assert(moduleName!moduleName == "std.traits"); } @@ -450,7 +531,20 @@ template moduleName(alias T) static assert(moduleName!(X12287!int.i) == "std.traits"); } -version (none) version (unittest) //Please uncomment me when changing moduleName to test global imports +// https://issues.dlang.org/show_bug.cgi?id=13741 +@safe unittest +{ + import std.ascii : isWhite; + static assert(moduleName!(isWhite) == "std.ascii"); + + struct Foo{void opCall(int){}} + static assert(moduleName!(Foo.opCall) == "std.traits"); + + @property void function(int) vf; + static assert(moduleName!(vf) == "std.traits"); +} + +version (none) @safe unittest //Please uncomment me when changing moduleName to test global imports { import core.sync.barrier; // global import static assert(!__traits(compiles, moduleName!(core.sync))); @@ -469,7 +563,7 @@ static assert(fullyQualifiedName!(const MyStruct[]) == "const(myModule.MyStruct[ ----------------- */ template fullyQualifiedName(T...) - if (T.length == 1) +if (T.length == 1) { static if (is(T)) @@ -484,13 +578,14 @@ template fullyQualifiedName(T...) static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName"); } -version (unittest) +version (StdUnittest) { // Used for both fqnType and fqnSym unittests private struct QualifiedNameTests { struct Inner { + bool value; } ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 ); @@ -607,8 +702,6 @@ private template fqnSym(alias T) private template fqnType(T, bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout) { - import std.format : format; - // Convenience tags enum { _const = 0, @@ -622,14 +715,17 @@ private template fqnType(T, string storageClassesString(uint psc)() @property { + import std.conv : text; + alias PSC = ParameterStorageClass; - return format("%s%s%s%s%s", + return text( psc & PSC.scope_ ? "scope " : "", psc & PSC.return_ ? "return " : "", + psc & PSC.in_ ? "in " : "", psc & PSC.out_ ? "out " : "", psc & PSC.ref_ ? "ref " : "", - psc & PSC.lazy_ ? "lazy " : "" + psc & PSC.lazy_ ? "lazy " : "", ); } @@ -658,7 +754,7 @@ private template fqnType(T, import std.range : zip; string result = join( - map!(a => format("%s%s", a[0], a[1]))( + map!(a => (a[0] ~ a[1]))( zip([staticMap!(storageClassesString, parameterStC)], [staticMap!(fullyQualifiedName, parameters)]) ), @@ -676,7 +772,7 @@ private template fqnType(T, enum linkage = functionLinkage!T; if (linkage != "D") - return format("extern(%s) ", linkage); + return "extern(" ~ linkage ~ ") "; else return ""; } @@ -689,16 +785,15 @@ private template fqnType(T, static if (attrs == FA.none) return ""; else - return format("%s%s%s%s%s%s%s%s", - attrs & FA.pure_ ? " pure" : "", - attrs & FA.nothrow_ ? " nothrow" : "", - attrs & FA.ref_ ? " ref" : "", - attrs & FA.property ? " @property" : "", - attrs & FA.trusted ? " @trusted" : "", - attrs & FA.safe ? " @safe" : "", - attrs & FA.nogc ? " @nogc" : "", - attrs & FA.return_ ? " return" : "" - ); + return + (attrs & FA.pure_ ? " pure" : "") + ~ (attrs & FA.nothrow_ ? " nothrow" : "") + ~ (attrs & FA.ref_ ? " ref" : "") + ~ (attrs & FA.property ? " @property" : "") + ~ (attrs & FA.trusted ? " @trusted" : "") + ~ (attrs & FA.safe ? " @safe" : "") + ~ (attrs & FA.nogc ? " @nogc" : "") + ~ (attrs & FA.return_ ? " return" : ""); } string addQualifiers(string typeString, @@ -707,15 +802,12 @@ private template fqnType(T, auto result = typeString; if (addShared) { - result = format("shared(%s)", result); + result = "shared(" ~ result ~")"; } if (addConst || addImmutable || addInout) { - result = format("%s(%s)", - addConst ? "const" : - addImmutable ? "immutable" : "inout", - result - ); + result = (addConst ? "const" : addImmutable ? "immutable" : "inout") + ~ "(" ~ result ~ ")"; } return result; } @@ -752,61 +844,62 @@ private template fqnType(T, } else static if (isStaticArray!T) { + import std.conv : to; enum fqnType = chain!( - format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length) + fqnType!(typeof(T.init[0]), qualifiers) ~ "[" ~ to!string(T.length) ~ "]" ); } else static if (isArray!T) { enum fqnType = chain!( - format("%s[]", fqnType!(typeof(T.init[0]), qualifiers)) + fqnType!(typeof(T.init[0]), qualifiers) ~ "[]" ); } else static if (isAssociativeArray!T) { enum fqnType = chain!( - format("%s[%s]", fqnType!(ValueType!T, qualifiers), fqnType!(KeyType!T, noQualifiers)) + fqnType!(ValueType!T, qualifiers) ~ '[' ~ fqnType!(KeyType!T, noQualifiers) ~ ']' ); } else static if (isSomeFunction!T) { static if (is(T F == delegate)) { - enum qualifierString = format("%s%s", - is(F == shared) ? " shared" : "", - is(F == inout) ? " inout" : - is(F == immutable) ? " immutable" : - is(F == const) ? " const" : "" - ); - enum formatStr = "%s%s delegate(%s)%s%s"; + enum qualifierString = + (is(F == shared) ? " shared" : "") + ~ (is(F == inout) ? " inout" : + is(F == immutable) ? " immutable" : + is(F == const) ? " const" : ""); enum fqnType = chain!( - format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers), - parametersTypeString!(T), functionAttributeString!T, qualifierString) + linkageString!T + ~ fqnType!(ReturnType!T, noQualifiers) + ~ " delegate(" ~ parametersTypeString!(T) ~ ")" + ~ functionAttributeString!T + ~ qualifierString ); } else { - static if (isFunctionPointer!T) - enum formatStr = "%s%s function(%s)%s"; - else - enum formatStr = "%s%s(%s)%s"; - enum fqnType = chain!( - format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers), - parametersTypeString!(T), functionAttributeString!T) + linkageString!T + ~ fqnType!(ReturnType!T, noQualifiers) + ~ (isFunctionPointer!T ? " function(" : "(") + ~ parametersTypeString!(T) ~ ")" + ~ functionAttributeString!T ); } } else static if (isPointer!T) { enum fqnType = chain!( - format("%s*", fqnType!(PointerTarget!T, qualifiers)) + fqnType!(PointerTarget!T, qualifiers) ~ "*" ); } else static if (is(T : __vector(V[N]), V, size_t N)) { + import std.conv : to; enum fqnType = chain!( - format("__vector(%s[%s])", fqnType!(V, qualifiers), N) + "__vector(" ~ fqnType!(V, qualifiers) ~ "[" ~ N.to!string ~ "])" ); } else @@ -889,12 +982,12 @@ private template fqnType(T, * 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 $(D opCall). Please note that $(D_KEYWORD ref) + * 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) +if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!func R == return)) alias ReturnType = R; @@ -949,11 +1042,11 @@ template ReturnType(func...) /*** Get, as a tuple, the types of the parameters to a function, a pointer -to function, a delegate, a struct with an $(D opCall), a pointer to a -struct with an $(D opCall), or a class with an $(D opCall). +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) +if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!func P == function)) alias Parameters = P; @@ -999,11 +1092,12 @@ alias ParameterTypeTuple = Parameters; } /** -Returns the number of arguments of function $(D func). +Returns the number of arguments of function `func`. arity is undefined for variadic functions. */ -template arity(alias func) - if ( isCallable!func && variadicFunctionStyle!func == Variadic.no ) +template arity(func...) +if (func.length == 1 && isCallable!func && + variadicFunctionStyle!func == Variadic.no) { enum size_t arity = Parameters!func.length; } @@ -1019,6 +1113,13 @@ template arity(alias func) static assert(!__traits(compiles, arity!variadicFoo)); } +// https://issues.dlang.org/show_bug.cgi?id=11389 +@safe unittest +{ + alias TheType = size_t function( string[] ); + static assert(arity!TheType == 1); +} + /** Get tuple, one per function parameter, of the storage classes of the parameters. Params: @@ -1032,17 +1133,18 @@ enum ParameterStorageClass : uint * These flags can be bitwise OR-ed together to represent complex storage * class. */ - none = 0, - scope_ = 1, /// ditto - out_ = 2, /// ditto - ref_ = 4, /// ditto - lazy_ = 8, /// ditto - return_ = 0x10, /// ditto + none = 0x00, + in_ = 0x01, /// ditto + ref_ = 0x02, /// ditto + out_ = 0x04, /// ditto + lazy_ = 0x08, /// ditto + scope_ = 0x10, /// ditto + return_ = 0x20, /// ditto } /// ditto template ParameterStorageClassTuple(func...) - if (func.length == 1 && isCallable!func) +if (func.length == 1 && isCallable!func) { alias Func = FunctionTypeOf!func; @@ -1073,22 +1175,32 @@ template ParameterStorageClassTuple(func...) { alias STC = ParameterStorageClass; // shorten the enum name - void func(ref int ctx, out real result, real param) + void func(ref int ctx, out real result, in real param, void* ptr) { } alias pstc = ParameterStorageClassTuple!func; - static assert(pstc.length == 3); // three parameters + static assert(pstc.length == 4); // number of parameters static assert(pstc[0] == STC.ref_); static assert(pstc[1] == STC.out_); - static assert(pstc[2] == STC.none); + version (none) + { + // TODO: When the DMD PR (dlang/dmd#11474) gets merged, + // remove the versioning and the second test + static assert(pstc[2] == STC.in_); + // This is the current behavior, before `in` is fixed to not be an alias + static assert(pstc[2] == STC.scope_); + } + static assert(pstc[3] == STC.none); } -/***************** - * Convert string tuple Attribs to ParameterStorageClass bits - * Params: - * Attribs = string tuple - * Returns: - * ParameterStorageClass bits +/** +Convert the result of `__traits(getParameterStorageClasses)` +to $(LREF ParameterStorageClass) `enum`s. + +Params: + Attribs = The return value of `__traits(getParameterStorageClasses)` +Returns: + The bitwise OR of the equivalent $(LREF ParameterStorageClass) `enum`s. */ template extractParameterStorageClassFlags(Attribs...) { @@ -1097,11 +1209,12 @@ template extractParameterStorageClassFlags(Attribs...) auto result = ParameterStorageClass.none; static if (Attribs.length > 0) { - foreach (attrib; [Attribs]) + static foreach (attrib; Attribs) { final switch (attrib) with (ParameterStorageClass) { case "scope": result |= scope_; break; + case "in": result |= in_; break; case "out": result |= out_; break; case "ref": result |= ref_; break; case "lazy": result |= lazy_; break; @@ -1118,6 +1231,28 @@ template extractParameterStorageClassFlags(Attribs...) }(); } +/// +@safe unittest +{ + static void func(ref int ctx, out real result); + + enum param1 = extractParameterStorageClassFlags!( + __traits(getParameterStorageClasses, func, 0) + ); + static assert(param1 == ParameterStorageClass.ref_); + + enum param2 = extractParameterStorageClassFlags!( + __traits(getParameterStorageClasses, func, 1) + ); + static assert(param2 == ParameterStorageClass.out_); + + enum param3 = extractParameterStorageClassFlags!( + __traits(getParameterStorageClasses, func, 0), + __traits(getParameterStorageClasses, func, 1) + ); + static assert(param3 == (ParameterStorageClass.ref_ | ParameterStorageClass.out_)); +} + @safe unittest { alias STC = ParameterStorageClass; @@ -1154,14 +1289,14 @@ template extractParameterStorageClassFlags(Attribs...) static assert(dglit_pstc.length == 1); static assert(dglit_pstc[0] == STC.ref_); - // Bugzilla 9317 + // https://issues.dlang.org/show_bug.cgi?id=9317 static inout(int) func(inout int param) { return param; } static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none); } @safe unittest { - // Bugzilla 14253 + // https://issues.dlang.org/show_bug.cgi?id=14253 static struct Foo { ref Foo opAssign(ref Foo rhs) return { return this; } } @@ -1174,7 +1309,7 @@ template extractParameterStorageClassFlags(Attribs...) Get, as a tuple, the identifiers of the parameters to a function symbol. */ template ParameterIdentifierTuple(func...) - if (func.length == 1 && isCallable!func) +if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!func PT == __parameters)) { @@ -1182,7 +1317,9 @@ template ParameterIdentifierTuple(func...) { static if (!isFunctionPointer!func && !isDelegate!func // Unnamed parameters yield CT error. - && is(typeof(__traits(identifier, PT[i .. i+1])))) + && is(typeof(__traits(identifier, PT[i .. i+1]))) + // Filter out unnamed args, which look like (Type) instead of (Type name). + && PT[i].stringof != PT[i .. i+1].stringof[1..$-1]) { enum Get = __traits(identifier, PT[i .. i+1]); } @@ -1194,7 +1331,7 @@ template ParameterIdentifierTuple(func...) } else { - static assert(0, func[0].stringof ~ "is not a function"); + static assert(0, func[0].stringof ~ " is not a function"); // Define dummy entities to avoid pointless errors template Get(size_t i) { enum Get = ""; } @@ -1219,6 +1356,16 @@ template ParameterIdentifierTuple(func...) static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); } +// https://issues.dlang.org/show_bug.cgi?id=19456 +@safe unittest +{ + struct SomeType {} + void foo(SomeType); + void bar(int); + static assert([ParameterIdentifierTuple!foo] == [""]); + static assert([ParameterIdentifierTuple!bar] == [""]); +} + @safe unittest { alias PIT = ParameterIdentifierTuple; @@ -1258,10 +1405,10 @@ template ParameterIdentifierTuple(func...) /** Get, as a tuple, the default value of the parameters to a function symbol. -If a parameter doesn't have the default value, $(D void) is returned instead. +If a parameter doesn't have the default value, `void` is returned instead. */ template ParameterDefaults(func...) - if (func.length == 1 && isCallable!func) +if (func.length == 1 && isCallable!func) { alias param_names = ParameterIdentifierTuple!func; static if (is(FunctionTypeOf!(func[0]) PT == __parameters)) @@ -1285,7 +1432,6 @@ template ParameterDefaults(func...) // like this. auto " ~ val ~ " = " ~ args ~ "[0]; auto " ~ ptr ~ " = &" ~ val ~ "; - // workaround Bugzilla 16582 return *" ~ ptr ~ "; }; "); @@ -1298,7 +1444,7 @@ template ParameterDefaults(func...) } else { - static assert(0, func[0].stringof ~ "is not a function"); + static assert(0, func[0].stringof ~ " is not a function"); // Define dummy entities to avoid pointless errors template Get(size_t i) { enum Get = ""; } @@ -1326,7 +1472,8 @@ template ParameterDefaults(func...) static assert( ParameterDefaults!foo[3] == 0); } -@safe unittest // issue 17192 +// https://issues.dlang.org/show_bug.cgi?id=17192 +@safe unittest { static void func(int i, int PT, int __pd_value, int __pd_val, int __args, int name, int args, int val, int ptr, int args_, int val_, int ptr_) @@ -1334,7 +1481,7 @@ template ParameterDefaults(func...) } alias Voids = ParameterDefaults!func; static assert(Voids.length == 12); - foreach (V; Voids) static assert(is(V == void)); + static foreach (V; Voids) static assert(is(V == void)); } /** @@ -1359,7 +1506,8 @@ alias ParameterDefaultValueTuple = ParameterDefaults; static assert( PDVT!baz[2] == "hello"); static assert(is(typeof(PDVT!baz) == typeof(AliasSeq!(void, 1, "hello")))); - // bug 10800 - property functions return empty string + // property functions return empty string + // https://issues.dlang.org/show_bug.cgi?id=10800 @property void foo(int x = 3) { } static assert(PDVT!foo.length == 1); static assert(PDVT!foo[0] == 3); @@ -1371,16 +1519,18 @@ alias ParameterDefaultValueTuple = ParameterDefaults; static immutable Colour white = Colour(255,255,255,255); } + // https://issues.dlang.org/show_bug.cgi?id=8106 void bug8106(Colour c = Colour.white) {} //pragma(msg, PDVT!bug8106); static assert(PDVT!bug8106[0] == Colour.white); + // https://issues.dlang.org/show_bug.cgi?id=16582 void bug16582(scope int* val = null) {} static assert(PDVT!bug16582[0] is null); } /** -Returns the FunctionAttribute mask for function $(D func). +Returns the FunctionAttribute mask for function `func`. See_Also: $(LREF hasFunctionAttributes) @@ -1405,11 +1555,12 @@ enum FunctionAttribute : uint shared_ = 1 << 11, /// ditto return_ = 1 << 12, /// ditto scope_ = 1 << 13, /// ditto + live = 1 << 14, /// ditto } /// ditto template functionAttributes(func...) - if (func.length == 1 && isCallable!func) +if (func.length == 1 && isCallable!func) { // @bug: workaround for opCall alias FuncSym = Select!(is(typeof(__traits(getFunctionAttributes, func))), @@ -1422,8 +1573,6 @@ template functionAttributes(func...) /// @safe unittest { - import std.traits : functionAttributes, FunctionAttribute; - alias FA = FunctionAttribute; // shorten the enum name real func(real x) pure nothrow @safe @@ -1458,6 +1607,8 @@ template functionAttributes(func...) int safeF() @safe { return 0; } int pureF() pure { return 0; } + + int liveF() @live { return 0; } } static assert(functionAttributes!(S.noF) == FA.system); @@ -1499,6 +1650,9 @@ template functionAttributes(func...) static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system)); static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system)); + static assert(functionAttributes!(S.liveF) == (FA.live | FA.system)); + static assert(functionAttributes!(typeof(S.liveF)) == (FA.live | FA.system)); + int pure_nothrow() nothrow pure; void safe_nothrow() @safe nothrow; static ref int static_ref_property() @property; @@ -1548,7 +1702,7 @@ private FunctionAttribute extractAttribFlags(Attribs...)() { auto res = FunctionAttribute.none; - foreach (attrib; Attribs) + static foreach (attrib; Attribs) { switch (attrib) with (FunctionAttribute) { @@ -1566,6 +1720,7 @@ private FunctionAttribute extractAttribFlags(Attribs...)() case "shared": res |= shared_; break; case "return": res |= return_; break; case "scope": res |= scope_; break; + case "@live": res |= live; break; default: assert(0, attrib); } } @@ -1587,14 +1742,14 @@ See_Also: $(LREF functionAttributes) */ template hasFunctionAttributes(args...) - if (args.length > 0 && isCallable!(args[0]) - && allSatisfy!(isSomeString, typeof(args[1 .. $]))) +if (args.length > 0 && isCallable!(args[0]) + && allSatisfy!(isSomeString, typeof(args[1 .. $]))) { enum bool hasFunctionAttributes = { import std.algorithm.searching : canFind; import std.range : only; enum funcAttribs = only(__traits(getFunctionAttributes, args[0])); - foreach (attribute; args[1 .. $]) + static foreach (attribute; args[1 .. $]) { if (!funcAttribs.canFind(attribute)) return false; @@ -1639,6 +1794,8 @@ template hasFunctionAttributes(args...) int safeF() @safe; int pureF() pure; + + int liveF() @live; } // true if no args passed @@ -1696,6 +1853,10 @@ template hasFunctionAttributes(args...) static assert(hasFunctionAttributes!(typeof(S.pureF), "pure", "@system")); static assert(!hasFunctionAttributes!(S.pureF, "pure", "@system", "ref")); + static assert(hasFunctionAttributes!(S.liveF, "@live", "@system")); + static assert(hasFunctionAttributes!(typeof(S.liveF), "@live", "@system")); + static assert(!hasFunctionAttributes!(S.liveF, "@live", "@system", "ref")); + int pure_nothrow() nothrow pure { return 0; } void safe_nothrow() @safe nothrow { } static ref int static_ref_property() @property { return *(new int); } @@ -1768,10 +1929,10 @@ template hasFunctionAttributes(args...) } /** -$(D true) if $(D func) is $(D @safe) or $(D @trusted). +`true` if `func` is `@safe` or `@trusted`. */ template isSafe(alias func) - if (isCallable!func) +if (isCallable!func) { enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 || (functionAttributes!func & FunctionAttribute.trusted) != 0; @@ -1804,9 +1965,9 @@ template isSafe(alias func) static assert(!isSafe!(Set.systemF)); //Functions - @safe static safeFunc() {} - @trusted static trustedFunc() {} - @system static systemFunc() {} + @safe static void safeFunc() {} + @trusted static void trustedFunc() {} + @system static void systemFunc() {} static assert( isSafe!safeFunc); static assert( isSafe!trustedFunc); @@ -1847,7 +2008,7 @@ template isSafe(alias func) /** -$(D true) if $(D func) is $(D @system). +`true` if `func` is `@system`. */ template isUnsafe(alias func) { @@ -1880,9 +2041,9 @@ template isUnsafe(alias func) static assert( isUnsafe!(Set.systemF)); //Functions - @safe static safeFunc() {} - @trusted static trustedFunc() {} - @system static systemFunc() {} + @safe static void safeFunc() {} + @trusted static void trustedFunc() {} + @system static void systemFunc() {} static assert(!isUnsafe!safeFunc); static assert(!isUnsafe!trustedFunc); @@ -1927,10 +2088,10 @@ Determine the linkage attribute of the function. Params: func = the function symbol, or the type of a function, delegate, or pointer to function Returns: - one of the strings "D", "C", "Windows", or "Objective-C" + one of the strings "D", "C", "C++", "Windows", "Objective-C", or "System". */ template functionLinkage(func...) - if (func.length == 1 && isCallable!func) +if (func.length == 1 && isCallable!func) { enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func); } @@ -1974,17 +2135,21 @@ Returns: */ enum Variadic { - no, /// Function is not variadic. - c, /// Function is a _C-style variadic function, which uses - /// core.stdc.stdarg - /// Function is a _D-style variadic function, which uses - d, /// __argptr and __arguments. - typesafe, /// Function is a typesafe variadic function. + /// Function is not variadic. + no, + /// Function is a _C-style variadic function, which uses + /// `core.stdc.stdarg` + c, + /// Function is a _D-style variadic function, which uses + /// `__argptr` and `__arguments`. + d, + /// Function is a typesafe variadic function. + typesafe, } /// ditto template variadicFunctionStyle(func...) - if (func.length == 1 && isCallable!func) +if (func.length == 1 && isCallable!func) { enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func); enum Variadic variadicFunctionStyle = @@ -2023,28 +2188,31 @@ template variadicFunctionStyle(func...) /** -Get the function type from a callable object $(D func). +Get the function type from a callable object `func`. -Using builtin $(D typeof) on a property function yields the types of the +Using builtin `typeof` on a property function yields the types of the property value, not of the property function itself. Still, -$(D FunctionTypeOf) is able to obtain function types of properties. +`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) +if (func.length == 1 && isCallable!func) { - static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate)) + 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)) + else static if (is(typeof(& func[0].opCall) Fobj == delegate) || 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)) + else static if ( + (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) || + (is(typeof(& func[0].opCall!()) Ftyp : Ftyp*) && is(Ftyp == function)) + ) { alias FunctionTypeOf = Ftyp; // HIT: callable type } @@ -2105,6 +2273,13 @@ template FunctionTypeOf(func...) static assert(is( FunctionTypeOf!stcall_val == typeof(test) )); static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) )); + struct TemplatedOpCallF { int opCall()(int) { return 0; } } + static assert(is( FunctionTypeOf!TemplatedOpCallF == typeof(TemplatedOpCallF.opCall!()) )); + + int foovar; + struct TemplatedOpCallDg { int opCall()() { return foovar; } } + static assert(is( FunctionTypeOf!TemplatedOpCallDg == typeof(TemplatedOpCallDg.opCall!()) )); + interface Overloads { void test(string); @@ -2112,7 +2287,7 @@ template FunctionTypeOf(func...) int test(int); int test() @property; } - alias ov = AliasSeq!(__traits(getVirtualFunctions, Overloads, "test")); + alias ov = __traits(getVirtualFunctions, Overloads, "test"); alias F_ov0 = FunctionTypeOf!(ov[0]); alias F_ov1 = FunctionTypeOf!(ov[1]); alias F_ov2 = FunctionTypeOf!(ov[2]); @@ -2139,7 +2314,7 @@ template FunctionTypeOf(func...) * attrs = The desired $(LREF FunctionAttribute)s of the result type. */ template SetFunctionAttributes(T, string linkage, uint attrs) - if (isFunctionPointer!T || isDelegate!T) +if (isFunctionPointer!T || isDelegate!T) { mixin({ import std.algorithm.searching : canFind; @@ -2206,6 +2381,8 @@ template SetFunctionAttributes(T, string linkage, uint attrs) result ~= " shared"; static if (attrs & FunctionAttribute.return_) result ~= " return"; + static if (attrs & FunctionAttribute.live) + result ~= " @live"; result ~= " SetFunctionAttributes;"; return result; @@ -2214,7 +2391,7 @@ template SetFunctionAttributes(T, string linkage, uint attrs) /// Ditto template SetFunctionAttributes(T, string linkage, uint attrs) - if (is(T == function)) +if (is(T == function)) { // To avoid a lot of syntactic headaches, we just use the above version to // operate on the corresponding function pointer type and then remove the @@ -2228,15 +2405,29 @@ template SetFunctionAttributes(T, string linkage, uint attrs) alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T); auto assumePure(T)(T t) - if (isFunctionPointer!T || isDelegate!T) + if (isFunctionPointer!T || isDelegate!T) { enum attrs = functionAttributes!T | FunctionAttribute.pure_; return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; } + + int f() + { + import core.thread : getpid; + return getpid(); + } + + int g() pure @trusted + { + auto pureF = assumePure(&f); + return pureF(); + } + assert(g() > 0); } -version (unittest) +version (StdUnittest) { +private: // Some function types to test. int sc(scope int, ref int, out int, lazy int, int); extern(System) int novar(); @@ -2249,11 +2440,11 @@ version (unittest) import std.algorithm.iteration : reduce; alias FA = FunctionAttribute; - foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle), + static foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle), typeof(&dstyle), typeof(&typesafe))) { - foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT)) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + static foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT)) + {{ enum linkage = functionLinkage!T; enum attrs = functionAttributes!T; @@ -2263,13 +2454,13 @@ version (unittest) // Check that all linkage types work (D-style variadics require D linkage). static if (variadicFunctionStyle!T != Variadic.d) { - foreach (newLinkage; AliasSeq!("D", "C", "Windows", "C++")) - { + static foreach (newLinkage; AliasSeq!("D", "C", "Windows", "C++")) + {{ alias New = SetFunctionAttributes!(T, newLinkage, attrs); static assert(functionLinkage!New == newLinkage, "Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~ " (got " ~ New.stringof ~ ")"); - } + }} } // Add @safe. @@ -2287,7 +2478,7 @@ version (unittest) // Strip all attributes again. alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none); static assert(is(T3 == T)); - }(); + }} } } @@ -2309,13 +2500,22 @@ Returns: `false` otherwise */ template isInnerClass(T) - if (is(T == class)) +if (is(T == class)) { - import std.meta : staticIndexOf; - static if (is(typeof(T.outer))) - enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) - && (staticIndexOf!(__traits(allMembers, T), "outer") == -1); + { + bool hasOuterMember(string[] members...) + { + foreach (m; members) + { + if (m == "outer") + return true; + } + return false; + } + enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && + !hasOuterMember(__traits(allMembers, T)); + } else enum isInnerClass = false; } @@ -2351,11 +2551,11 @@ template isInnerClass(T) } /** -Determines whether $(D T) has its own context pointer. -$(D T) must be either $(D class), $(D struct), or $(D union). +Determines whether `T` has its own context pointer. +`T` must be either `class`, `struct`, or `union`. */ template isNested(T) - if (is(T == class) || is(T == struct) || is(T == union)) +if (is(T == class) || is(T == struct) || is(T == union)) { enum isNested = __traits(isNested, T); } @@ -2372,19 +2572,19 @@ template isNested(T) } /** -Determines whether $(D T) or any of its representation types +Determines whether `T` or any of its representation types have a context pointer. */ template hasNested(T) { - import std.meta : anySatisfy, Filter; + import std.meta : Filter; static if (isStaticArray!T && T.length) enum hasNested = hasNested!(typeof(T.init[0])); else static if (is(T == class) || is(T == struct) || is(T == union)) { // prevent infinite recursion for class with member of same type - enum notSame(U) = !is(Unqual!T == Unqual!U); + enum notSame(U) = !is(immutable T == immutable U); enum hasNested = isNested!T || anySatisfy!(.hasNested, Filter!(notSame, Fields!T)); } @@ -2467,14 +2667,17 @@ template hasNested(T) * This consists of the fields that take up memory space, * excluding the hidden fields like the virtual function * table pointer or a context pointer for nested types. - * If $(D T) isn't a struct, class, or union returns a tuple - * with one element $(D T). + * If `T` isn't a struct, class, interface or union returns a tuple + * with one element `T`. + * + * History: + * - Returned `AliasSeq!(Interface)` for interfaces prior to 2.097 */ template Fields(T) { static if (is(T == struct) || is(T == union)) alias Fields = typeof(T.tupleof[0 .. $ - isNested!T]); - else static if (is(T == class)) + else static if (is(T == class) || is(T == interface)) alias Fields = typeof(T.tupleof); else alias Fields = AliasSeq!T; @@ -2483,6 +2686,7 @@ template Fields(T) /// @safe unittest { + import std.meta : AliasSeq; struct S { int x; float y; } static assert(is(Fields!S == AliasSeq!(int, float))); } @@ -2512,8 +2716,10 @@ alias FieldTypeTuple = Fields; class NestedClass { int a; void f() { ++i; } } static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int)); -} + static interface I {} + static assert(is(Fields!I == AliasSeq!())); +} //Required for FieldNameTuple private enum NameOf(alias T) = T.stringof; @@ -2522,15 +2728,20 @@ private enum NameOf(alias T) = T.stringof; * Get as an expression tuple the names of the fields of a struct, class, or * union. This consists of the fields that take up memory space, excluding the * hidden fields like the virtual function table pointer or a context pointer - * for nested types. If $(D T) isn't a struct, class, or union returns an - * expression tuple with an empty string. + * for nested types. + * Inherited fields (for classes) are not included. + * If `T` isn't a struct, class, interface or union, an + * expression tuple with an empty string is returned. + * + * History: + * - Returned `AliasSeq!""` for interfaces prior to 2.097 */ template FieldNameTuple(T) { import std.meta : staticMap; static if (is(T == struct) || is(T == union)) alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]); - else static if (is(T == class)) + else static if (is(T == class) || is(T == interface)) alias FieldNameTuple = staticMap!(NameOf, T.tupleof); else alias FieldNameTuple = AliasSeq!""; @@ -2539,6 +2750,7 @@ template FieldNameTuple(T) /// @safe unittest { + import std.meta : AliasSeq; struct S { int x; float y; } static assert(FieldNameTuple!S == AliasSeq!("x", "y")); static assert(FieldNameTuple!int == AliasSeq!""); @@ -2554,6 +2766,15 @@ template FieldNameTuple(T) static struct StaticStruct2 { int a, b; } static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b")); + static class StaticClass1 { } + static assert(is(FieldNameTuple!StaticClass1 == AliasSeq!())); + + static class StaticClass2 : StaticClass1 { int a, b; } + static assert(FieldNameTuple!StaticClass2 == AliasSeq!("a", "b")); + + static class StaticClass3 : StaticClass2 { int c; } + static assert(FieldNameTuple!StaticClass3 == AliasSeq!("c")); + int i; struct NestedStruct1 { void f() { ++i; } } @@ -2564,6 +2785,9 @@ template FieldNameTuple(T) class NestedClass { int a; void f() { ++i; } } static assert(FieldNameTuple!NestedClass == AliasSeq!"a"); + + interface I {} + static assert(FieldNameTuple!I == AliasSeq!()); } @@ -2573,41 +2797,13 @@ topological order. */ template RepresentationTypeTuple(T) { - template Impl(T...) - { - static if (T.length == 0) - { - alias Impl = AliasSeq!(); - } - else - { - import std.typecons : Rebindable; - - static if (is(T[0] R: Rebindable!R)) - { - alias Impl = Impl!(Impl!R, T[1 .. $]); - } - else static if (is(T[0] == struct) || is(T[0] == union)) - { - // @@@BUG@@@ this should work - //alias .RepresentationTypes!(T[0].tupleof) - // RepresentationTypes; - alias Impl = Impl!(FieldTypeTuple!(T[0]), T[1 .. $]); - } - else - { - alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $])); - } - } - } - static if (is(T == struct) || is(T == union) || is(T == class)) { - alias RepresentationTypeTuple = Impl!(FieldTypeTuple!T); + alias RepresentationTypeTuple = staticMapMeta!(RepresentationTypeTupleImpl, FieldTypeTuple!T); } else { - alias RepresentationTypeTuple = Impl!T; + alias RepresentationTypeTuple = RepresentationTypeTupleImpl!T; } } @@ -2645,7 +2841,7 @@ template RepresentationTypeTuple(T) alias R1 = RepresentationTypeTuple!C; static assert(R1.length == 2 && is(R1[0] == int) && is(R1[1] == float)); - /* Issue 6642 */ + /* https://issues.dlang.org/show_bug.cgi?id=6642 */ import std.typecons : Rebindable; struct S5 { int a; Rebindable!(immutable Object) b; } @@ -2653,39 +2849,58 @@ template RepresentationTypeTuple(T) static assert(R2.length == 2 && is(R2[0] == int) && is(R2[1] == immutable(Object))); } -/* -Statically evaluates to $(D true) if and only if $(D T)'s -representation contains at least one field of pointer or array type. -Members of class types are not considered raw pointers. Pointers to -immutable objects are not considered raw aliasing. -*/ -private template hasRawAliasing(T...) +@safe unittest { - template Impl(T...) + struct VeryLargeType { - static if (T.length == 0) + import std.format : format; + import std.range : iota; + + static foreach (i; 500.iota) { - enum Impl = false; + mixin(format!"int v%s;"(i)); } - else - { - static if (is(T[0] foo : U*, U) && !isFunctionPointer!(T[0])) - enum has = !is(U == immutable); - else static if (is(T[0] foo : U[], U) && !isStaticArray!(T[0])) - enum has = !is(U == immutable); - else static if (isAssociativeArray!(T[0])) - enum has = !is(T[0] == immutable); - else - enum has = false; + } - enum Impl = has || Impl!(T[1 .. $]); - } + alias BigList = RepresentationTypeTuple!VeryLargeType; +} + +private template RepresentationTypeTupleImpl(T) +{ + import std.typecons : Rebindable; + + static if (is(T R: Rebindable!R)) + { + alias RepresentationTypeTupleImpl + = staticMapMeta!(.RepresentationTypeTupleImpl, RepresentationTypeTupleImpl!R); + } + else static if (is(T == struct) || is(T == union)) + { + // @@@BUG@@@ this should work + //alias .RepresentationTypes!(T[0].tupleof) + // RepresentationTypes; + alias RepresentationTypeTupleImpl + = staticMapMeta!(.RepresentationTypeTupleImpl, FieldTypeTuple!(T)); + } + else + { + alias RepresentationTypeTupleImpl + = AliasSeq!T; } +} - enum hasRawAliasing = Impl!(RepresentationTypeTuple!T); +/* +Statically evaluates to `true` if and only if `T`'s +representation contains at least one field of pointer or array type. +Members of class types are not considered raw pointers. Pointers to +immutable objects are not considered raw aliasing. +*/ +private template hasRawAliasing(T) +{ + enum hasRawAliasing = anySatisfy!(hasRawAliasingImpl, RepresentationTypeTuple!T); } -/// +// @safe unittest { // simple types @@ -2703,6 +2918,16 @@ private template hasRawAliasing(T...) static assert(!hasRawAliasing!S2); } +// https://issues.dlang.org/show_bug.cgi?id=19228 +@safe unittest +{ + static struct C + { + int*[1] a; + } + static assert(hasRawAliasing!C); +} + @safe unittest { // struct with a pointer member @@ -2749,39 +2974,36 @@ private template hasRawAliasing(T...) static assert(!hasRawAliasing!(immutable(int[string]))); } +private template hasRawAliasingImpl(T) +{ + static if (is(T foo : U*, U) && !isFunctionPointer!T) + enum hasRawAliasingImpl = !is(U == immutable); + else static if (is(T foo : U[N], U, size_t N)) + // separate static ifs to avoid forward reference + static if (is(U == class) || is(U == interface)) + enum hasRawAliasingImpl = false; + else + enum hasRawAliasingImpl = hasRawAliasingImpl!U; + else static if (is(T foo : U[], U) && !isStaticArray!(T)) + enum hasRawAliasingImpl = !is(U == immutable); + else static if (isAssociativeArray!(T)) + enum hasRawAliasingImpl = !is(T == immutable); + else + enum hasRawAliasingImpl = false; +} + /* -Statically evaluates to $(D true) if and only if $(D T)'s +Statically evaluates to `true` if and only if `T`'s representation contains at least one non-shared field of pointer or array type. Members of class types are not considered raw pointers. Pointers to immutable objects are not considered raw aliasing. */ -private template hasRawUnsharedAliasing(T...) +private template hasRawUnsharedAliasing(T) { - template Impl(T...) - { - static if (T.length == 0) - { - enum Impl = false; - } - else - { - static if (is(T[0] foo : U*, U) && !isFunctionPointer!(T[0])) - enum has = !is(U == immutable) && !is(U == shared); - else static if (is(T[0] foo : U[], U) && !isStaticArray!(T[0])) - enum has = !is(U == immutable) && !is(U == shared); - else static if (isAssociativeArray!(T[0])) - enum has = !is(T[0] == immutable) && !is(T[0] == shared); - else - enum has = false; - - enum Impl = has || Impl!(T[1 .. $]); - } - } - - enum hasRawUnsharedAliasing = Impl!(RepresentationTypeTuple!T); + enum hasRawUnsharedAliasing = anySatisfy!(hasRawUnsharedAliasingImpl, RepresentationTypeTuple!T); } -/// +// @safe unittest { // simple types @@ -2931,81 +3153,64 @@ private template hasRawUnsharedAliasing(T...) static assert(!hasRawUnsharedAliasing!S28); } +private template hasRawUnsharedAliasingImpl(T) +{ + static if (is(T foo : U*, U) && !isFunctionPointer!T) + enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared); + else static if (is(T foo : U[], U) && !isStaticArray!T) + enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared); + else static if (isAssociativeArray!T) + enum hasRawUnsharedAliasingImpl = !is(T == immutable) && !is(T == shared); + else + enum hasRawUnsharedAliasingImpl = false; +} + /* -Statically evaluates to $(D true) if and only if $(D T)'s +Statically evaluates to `true` if and only if `T`'s representation includes at least one non-immutable object reference. */ -private template hasObjects(T...) +private template hasObjects(T) { - static if (T.length == 0) - { - enum hasObjects = false; - } - else static if (is(T[0] == struct)) + static if (is(T == struct)) { - enum hasObjects = hasObjects!( - RepresentationTypeTuple!(T[0]), T[1 .. $]); + enum hasObjects = anySatisfy!(.hasObjects, RepresentationTypeTuple!T); } else { - enum hasObjects = ((is(T[0] == class) || is(T[0] == interface)) - && !is(T[0] == immutable)) || hasObjects!(T[1 .. $]); + enum hasObjects = (is(T == class) || is(T == interface)) && !is(T == immutable); } } /* -Statically evaluates to $(D true) if and only if $(D T)'s +Statically evaluates to `true` if and only if `T`'s representation includes at least one non-immutable non-shared object reference. */ -private template hasUnsharedObjects(T...) +private template hasUnsharedObjects(T) { - static if (T.length == 0) - { - enum hasUnsharedObjects = false; - } - else static if (is(T[0] == struct)) + static if (is(T == struct)) { - enum hasUnsharedObjects = hasUnsharedObjects!( - RepresentationTypeTuple!(T[0]), T[1 .. $]); + enum hasUnsharedObjects = anySatisfy!(.hasUnsharedObjects, RepresentationTypeTuple!T); } else { - enum hasUnsharedObjects = ((is(T[0] == class) || is(T[0] == interface)) && - !is(T[0] == immutable) && !is(T[0] == shared)) || - hasUnsharedObjects!(T[1 .. $]); + enum hasUnsharedObjects = (is(T == class) || is(T == interface)) && + !is(T == immutable) && !is(T == shared); } } /** -Returns $(D true) if and only if $(D T)'s representation includes at -least one of the following: $(OL $(LI a raw pointer $(D U*) and $(D U) -is not immutable;) $(LI an array $(D U[]) and $(D U) is not -immutable;) $(LI a reference to a class or interface type $(D C) and $(D C) is +Returns `true` if and only if `T`'s representation includes at +least one of the following: $(OL $(LI a raw pointer `U*` and `U` +is not immutable;) $(LI an array `U[]` and `U` is not +immutable;) $(LI a reference to a class or interface type `C` and `C` is not immutable.) $(LI an associative array that is not immutable.) $(LI a delegate.)) */ template hasAliasing(T...) { - import std.meta : anySatisfy; - import std.typecons : Rebindable; - - static if (T.length && is(T[0] : Rebindable!R, R)) - { - enum hasAliasing = hasAliasing!(R, T[1 .. $]); - } - else - { - template isAliasingDelegate(T) - { - enum isAliasingDelegate = isDelegate!T - && !is(T == immutable) - && !is(FunctionTypeOf!T == immutable); - } - enum hasAliasing = hasRawAliasing!T || hasObjects!T || - anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T); - } + enum hasAliasing = anySatisfy!(hasAliasingImpl, T); } /// @@ -3083,18 +3288,44 @@ template hasAliasing(T...) static assert( hasAliasing!S12); static assert( hasAliasing!S13); static assert(!hasAliasing!S14); + + class S15 { S15[1] a; } + static assert( hasAliasing!S15); + static assert(!hasAliasing!(immutable(S15))); +} + +private template hasAliasingImpl(T) +{ + import std.typecons : Rebindable; + + static if (is(T : Rebindable!R, R)) + { + enum hasAliasingImpl = hasAliasingImpl!R; + } + else + { + template isAliasingDelegate(T) + { + enum isAliasingDelegate = isDelegate!T + && !is(T == immutable) + && !is(FunctionTypeOf!T == immutable); + } + enum hasAliasingImpl = hasRawAliasing!T || hasObjects!T || + anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T); + } } + /** -Returns $(D true) if and only if $(D T)'s representation includes at -least one of the following: $(OL $(LI a raw pointer $(D U*);) $(LI an -array $(D U[]);) $(LI a reference to a class type $(D C).) -$(LI an associative array.) $(LI a delegate.)) +Returns `true` if and only if `T`'s representation includes at +least one of the following: $(OL $(LI a raw pointer `U*`;) $(LI an +array `U[]`;) $(LI a reference to a class type `C`;) +$(LI an associative array;) $(LI a delegate;) +$(LI a [context pointer][isNested].)) */ template hasIndirections(T) { - import std.meta : anySatisfy; static if (is(T == struct) || is(T == union)) - enum hasIndirections = anySatisfy!(.hasIndirections, FieldTypeTuple!T); + enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof)); else static if (isStaticArray!T && is(T : E[N], E, size_t N)) enum hasIndirections = is(E == void) ? true : hasIndirections!E; else static if (isFunctionPointer!T) @@ -3175,9 +3406,13 @@ template hasIndirections(T) static assert( hasIndirections!S24); static assert( hasIndirections!S25); static assert( hasIndirections!S26); + int local; + struct HasContextPointer { int opCall() { return ++local; } } + static assert(hasIndirections!HasContextPointer); } -@safe unittest //12000 +// https://issues.dlang.org/show_bug.cgi?id=12000 +@safe unittest { static struct S(T) { @@ -3193,45 +3428,17 @@ template hasIndirections(T) } /** -Returns $(D true) if and only if $(D T)'s representation includes at -least one of the following: $(OL $(LI a raw pointer $(D U*) and $(D U) -is not immutable or shared;) $(LI an array $(D U[]) and $(D U) is not -immutable or shared;) $(LI a reference to a class type $(D C) and -$(D C) is not immutable or shared.) $(LI an associative array that is not +Returns `true` if and only if `T`'s representation includes at +least one of the following: $(OL $(LI a raw pointer `U*` and `U` +is not immutable or shared;) $(LI an array `U[]` and `U` is not +immutable or shared;) $(LI a reference to a class type `C` and +`C` is not immutable or shared.) $(LI an associative array that is not immutable or shared.) $(LI a delegate that is not shared.)) */ template hasUnsharedAliasing(T...) { - import std.meta : anySatisfy; - import std.typecons : Rebindable; - - static if (!T.length) - { - enum hasUnsharedAliasing = false; - } - else static if (is(T[0] R: Rebindable!R)) - { - enum hasUnsharedAliasing = hasUnsharedAliasing!R; - } - else - { - template unsharedDelegate(T) - { - enum bool unsharedDelegate = isDelegate!T - && !is(T == shared) - && !is(T == shared) - && !is(T == immutable) - && !is(FunctionTypeOf!T == shared) - && !is(FunctionTypeOf!T == immutable); - } - - enum hasUnsharedAliasing = - hasRawUnsharedAliasing!(T[0]) || - anySatisfy!(unsharedDelegate, RepresentationTypeTuple!(T[0])) || - hasUnsharedObjects!(T[0]) || - hasUnsharedAliasing!(T[1..$]); - } + enum hasUnsharedAliasing = anySatisfy!(hasUnsharedAliasingImpl, T); } /// @@ -3256,7 +3463,7 @@ template hasUnsharedAliasing(T...) @safe unittest { - /* Issue 6642 */ + /* https://issues.dlang.org/show_bug.cgi?id=6642 */ import std.typecons : Rebindable; struct S8 { int a; Rebindable!(immutable Object) b; } static assert(!hasUnsharedAliasing!S8); @@ -3303,7 +3510,7 @@ template hasUnsharedAliasing(T...) static assert(!hasUnsharedAliasing!(Rebindable!(shared Object))); static assert( hasUnsharedAliasing!(Rebindable!Object)); - /* Issue 6979 */ + /* https://issues.dlang.org/show_bug.cgi?id=6979 */ static assert(!hasUnsharedAliasing!(int, shared(int)*)); static assert( hasUnsharedAliasing!(int, int*)); static assert( hasUnsharedAliasing!(int, const(int)[])); @@ -3370,29 +3577,43 @@ template hasUnsharedAliasing(T...) static assert(!hasUnsharedAliasing!S20); } +private template hasUnsharedAliasingImpl(T) +{ + import std.typecons : Rebindable; + + static if (is(T R: Rebindable!R)) + { + enum hasUnsharedAliasingImpl = hasUnsharedAliasingImpl!R; + } + else + { + template unsharedDelegate(T) + { + enum bool unsharedDelegate = isDelegate!T + && !is(T == shared) + && !is(T == immutable) + && !is(FunctionTypeOf!T == shared) + && !is(FunctionTypeOf!T == immutable); + } + + enum hasUnsharedAliasingImpl = + hasRawUnsharedAliasing!T || + anySatisfy!(unsharedDelegate, RepresentationTypeTuple!T) || + hasUnsharedObjects!T; + } +} + /** - True if $(D S) or any type embedded directly in the representation of $(D S) + True if `S` or any type embedded directly in the representation of `S` defines an elaborate copy constructor. Elaborate copy constructors are - introduced by defining $(D this(this)) for a $(D struct). + introduced by defining `this(this)` for a `struct`. Classes and unions never have elaborate copy constructors. */ template hasElaborateCopyConstructor(S) { - import std.meta : anySatisfy; - static if (isStaticArray!S && S.length) - { - enum bool hasElaborateCopyConstructor = hasElaborateCopyConstructor!(typeof(S.init[0])); - } - else static if (is(S == struct)) - { - enum hasElaborateCopyConstructor = hasMember!(S, "__postblit") - || anySatisfy!(.hasElaborateCopyConstructor, FieldTypeTuple!S); - } - else - { - enum bool hasElaborateCopyConstructor = false; - } + import core.internal.traits : hasElabCCtor = hasElaborateCopyConstructor; + alias hasElaborateCopyConstructor = hasElabCCtor!(S); } /// @@ -3420,13 +3641,13 @@ template hasElaborateCopyConstructor(S) } /** - True if $(D S) or any type directly embedded in the representation of $(D S) + True if `S` or any type directly embedded in the representation of `S` defines an elaborate assignment. Elaborate assignments are introduced by - defining $(D opAssign(typeof(this))) or $(D opAssign(ref typeof(this))) - for a $(D struct) or when there is a compiler-generated $(D opAssign). + defining `opAssign(typeof(this))` or $(D opAssign(ref typeof(this))) + for a `struct` or when there is a compiler-generated `opAssign`. - A type $(D S) gets compiler-generated $(D opAssign) in case it has - an elaborate copy constructor or elaborate destructor. + A type `S` gets compiler-generated `opAssign` if it has + an elaborate destructor. Classes and unions never have elaborate assignments. @@ -3436,7 +3657,6 @@ template hasElaborateCopyConstructor(S) */ template hasElaborateAssign(S) { - import std.meta : anySatisfy; static if (isStaticArray!S && S.length) { enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0])); @@ -3513,30 +3733,18 @@ template hasElaborateAssign(S) } /** - True if $(D S) or any type directly embedded in the representation - of $(D S) defines an elaborate destructor. Elaborate destructors - are introduced by defining $(D ~this()) for a $(D + True if `S` or any type directly embedded in the representation + of `S` defines an elaborate destructor. Elaborate destructors + are introduced by defining `~this()` for a $(D struct). Classes and unions never have elaborate destructors, even - though classes may define $(D ~this()). + though classes may define `~this()`. */ template hasElaborateDestructor(S) { - import std.meta : anySatisfy; - static if (isStaticArray!S && S.length) - { - enum bool hasElaborateDestructor = hasElaborateDestructor!(typeof(S.init[0])); - } - else static if (is(S == struct)) - { - enum hasElaborateDestructor = hasMember!(S, "__dtor") - || anySatisfy!(.hasElaborateDestructor, FieldTypeTuple!S); - } - else - { - enum bool hasElaborateDestructor = false; - } + import core.internal.traits : hasElabDest = hasElaborateDestructor; + alias hasElaborateDestructor = hasElabDest!(S); } /// @@ -3563,11 +3771,52 @@ template hasElaborateDestructor(S) static assert( hasElaborateDestructor!S7); } +/** + True if `S` or any type embedded directly in the representation of `S` + defines elaborate move semantics. Elaborate move semantics are + introduced by defining `opPostMove(ref typeof(this))` for a `struct`. + + Classes and unions never have elaborate move semantics. + */ +template hasElaborateMove(S) +{ + import core.internal.traits : hasElabMove = hasElaborateMove; + alias hasElaborateMove = hasElabMove!(S); +} + +/// +@safe unittest +{ + static assert(!hasElaborateMove!int); + + static struct S1 { } + static struct S2 { void opPostMove(ref S2) {} } + static struct S3 { void opPostMove(inout ref S3) inout {} } + static struct S4 { void opPostMove(const ref S4) {} } + static struct S5 { void opPostMove(S5) {} } + static struct S6 { void opPostMove(int) {} } + static struct S7 { S3[1] field; } + static struct S8 { S3[] field; } + static struct S9 { S3[0] field; } + static struct S10 { @disable this(); S3 field; } + static assert(!hasElaborateMove!S1); + static assert( hasElaborateMove!S2); + static assert( hasElaborateMove!S3); + static assert( hasElaborateMove!(immutable S3)); + static assert( hasElaborateMove!S4); + static assert(!hasElaborateMove!S5); + static assert(!hasElaborateMove!S6); + static assert( hasElaborateMove!S7); + static assert(!hasElaborateMove!S8); + static assert(!hasElaborateMove!S9); + static assert( hasElaborateMove!S10); +} + package alias Identity(alias A) = A; /** - Yields $(D true) if and only if $(D T) is an aggregate that defines - a symbol called $(D name). + Yields `true` if and only if `T` is an aggregate that defines + a symbol called `name`. */ enum hasMember(T, string name) = __traits(hasMember, T, name); @@ -3587,7 +3836,7 @@ enum hasMember(T, string name) = __traits(hasMember, T, name); @safe unittest { - // 8321 + // https://issues.dlang.org/show_bug.cgi?id=8321 struct S { int x; void f(){} @@ -3630,20 +3879,25 @@ enum hasMember(T, string name) = __traits(hasMember, T, name); * Whether the symbol represented by the string, member, exists and is a static member of T. * * Params: - * T = Type containing symbol $(D member). - * member = Name of symbol to test that resides in $(D T). + * T = Type containing symbol `member`. + * member = Name of symbol to test that resides in `T`. * * Returns: - * $(D true) iff $(D member) exists and is static. + * `true` iff `member` exists and is static. */ template hasStaticMember(T, string member) { static if (__traits(hasMember, T, member)) { + static if (isPointer!T) + alias U = PointerTarget!T; + else + alias U = T; + import std.meta : Alias; - alias sym = Alias!(__traits(getMember, T, member)); + alias sym = Alias!(__traits(getMember, U, member)); - static if (__traits(getOverloads, T, member).length == 0) + static if (__traits(getOverloads, U, member).length == 0) enum bool hasStaticMember = __traits(compiles, &sym); else enum bool hasStaticMember = __traits(isStaticFunction, sym); @@ -3696,7 +3950,7 @@ template hasStaticMember(T, string member) static void f(); static void f2() pure nothrow @nogc @safe; - shared void g(); + void g() shared; static void function() fp; __gshared void function() gfp; @@ -3734,7 +3988,7 @@ template hasStaticMember(T, string member) static void f(); static void f2() pure nothrow @nogc @safe; - shared void g() { } + void g() shared { } static void function() fp; __gshared void function() gfp; @@ -3753,6 +4007,7 @@ template hasStaticMember(T, string member) static @property int sp(); } + static assert(!hasStaticMember!(S, "na")); static assert(!hasStaticMember!(S, "X")); static assert(!hasStaticMember!(S, "Y")); static assert(!hasStaticMember!(S, "Y.i")); @@ -3777,6 +4032,7 @@ template hasStaticMember(T, string member) static assert(!hasStaticMember!(S, "p")); static assert( hasStaticMember!(S, "sp")); + static assert(!hasStaticMember!(C, "na")); static assert(!hasStaticMember!(C, "X")); static assert(!hasStaticMember!(C, "Y")); static assert(!hasStaticMember!(C, "Y.i")); @@ -3788,8 +4044,8 @@ template hasStaticMember(T, string member) static assert( hasStaticMember!(C, "sy")); static assert( hasStaticMember!(C, "f")); static assert( hasStaticMember!(C, "f2")); - static assert(!hasStaticMember!(S, "dm")); - static assert( hasStaticMember!(S, "sd")); + static assert(!hasStaticMember!(C, "dm")); + static assert( hasStaticMember!(C, "sd")); static assert(!hasStaticMember!(C, "g")); static assert( hasStaticMember!(C, "fp")); static assert( hasStaticMember!(C, "gfp")); @@ -3800,75 +4056,65 @@ template hasStaticMember(T, string member) static assert( hasStaticMember!(C, "iosf")); static assert(!hasStaticMember!(C, "p")); static assert( hasStaticMember!(C, "sp")); + + alias P = S*; + static assert(!hasStaticMember!(P, "na")); + static assert(!hasStaticMember!(P, "X")); + static assert(!hasStaticMember!(P, "Y")); + static assert(!hasStaticMember!(P, "Y.i")); + static assert(!hasStaticMember!(P, "S")); + static assert(!hasStaticMember!(P, "C")); + static assert( hasStaticMember!(P, "sx")); + static assert( hasStaticMember!(P, "gx")); + static assert(!hasStaticMember!(P, "y")); + static assert( hasStaticMember!(P, "sy")); + static assert( hasStaticMember!(P, "f")); + static assert( hasStaticMember!(P, "f2")); + static assert(!hasStaticMember!(P, "dm")); + static assert( hasStaticMember!(P, "sd")); + static assert(!hasStaticMember!(P, "g")); + static assert( hasStaticMember!(P, "fp")); + static assert( hasStaticMember!(P, "gfp")); + static assert(!hasStaticMember!(P, "fpm")); + static assert(!hasStaticMember!(P, "m")); + static assert(!hasStaticMember!(P, "m2")); + static assert(!hasStaticMember!(P, "iom")); + static assert( hasStaticMember!(P, "iosf")); + static assert(!hasStaticMember!(P, "p")); + static assert( hasStaticMember!(P, "sp")); } /** -Retrieves the members of an enumerated type $(D enum E). +Retrieves the members of an enumerated type `enum E`. Params: - E = An enumerated type. $(D E) may have duplicated values. + E = An enumerated type. `E` may have duplicated values. Returns: - Static tuple composed of the members of the enumerated type $(D E). - The members are arranged in the same order as declared in $(D E). + Static tuple composed of the members of the enumerated type `E`. + The members are arranged in the same order as declared in `E`. + The name of the enum can be found by querying the compiler for the + name of the identifier, i.e. `__traits(identifier, EnumMembers!MyEnum[i])`. + For enumerations with unique values, $(REF to, std,conv) can also be used. Note: - An enum can have multiple members which have the same value. If you want - to use EnumMembers to e.g. generate switch cases at compile-time, - you should use the $(REF NoDuplicates, std,meta) template to avoid - generating duplicate switch cases. + An enum can have multiple members which have the same value. If you want + to use EnumMembers to e.g. generate switch cases at compile-time, + you should use the $(REF NoDuplicates, std,meta) template to avoid + generating duplicate switch cases. Note: - Returned values are strictly typed with $(D E). Thus, the following code - does not work without the explicit cast: + Returned values are strictly typed with `E`. Thus, the following code + does not work without the explicit cast: -------------------- enum E : int { a, b, c } int[] abc = cast(int[]) [ EnumMembers!E ]; -------------------- - Cast is not necessary if the type of the variable is inferred. See the - example below. - -Example: - Creating an array of enumerated values: --------------------- -enum Sqrts : real -{ - one = 1, - two = 1.41421, - three = 1.73205, -} -auto sqrts = [ EnumMembers!Sqrts ]; -assert(sqrts == [ Sqrts.one, Sqrts.two, Sqrts.three ]); --------------------- - - A generic function $(D rank(v)) in the following example uses this - template for finding a member $(D e) in an enumerated type $(D E). --------------------- -// Returns i if e is the i-th enumerator of E. -size_t rank(E)(E e) - if (is(E == enum)) -{ - foreach (i, member; EnumMembers!E) - { - if (e == member) - return i; - } - assert(0, "Not an enum member"); -} - -enum Mode -{ - read = 1, - write = 2, - map = 4, -} -assert(rank(Mode.read ) == 0); -assert(rank(Mode.write) == 1); -assert(rank(Mode.map ) == 2); --------------------- + Cast is not necessary if the type of the variable is inferred. See the + example below. */ template EnumMembers(E) - if (is(E == enum)) +if (is(E == enum)) { import std.meta : AliasSeq; // Supply the specified identifier to an constant value. @@ -3916,6 +4162,81 @@ template EnumMembers(E) alias EnumMembers = EnumSpecificMembers!(__traits(allMembers, E)); } +/// Create an array of enumerated values +@safe unittest +{ + enum Sqrts : real + { + one = 1, + two = 1.41421, + three = 1.73205 + } + auto sqrts = [EnumMembers!Sqrts]; + assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]); +} + +/** +A generic function `rank(v)` in the following example uses this +template for finding a member `e` in an enumerated type `E`. + */ +@safe unittest +{ + // Returns i if e is the i-th enumerator of E. + static size_t rank(E)(E e) + if (is(E == enum)) + { + static foreach (i, member; EnumMembers!E) + { + if (e == member) + return i; + } + assert(0, "Not an enum member"); + } + + enum Mode + { + read = 1, + write = 2, + map = 4 + } + assert(rank(Mode.read) == 0); + assert(rank(Mode.write) == 1); + assert(rank(Mode.map) == 2); +} + +/** +Use EnumMembers to generate a switch statement using static foreach. +*/ + +@safe unittest +{ + import std.conv : to; + class FooClass + { + string calledMethod; + void foo() @safe { calledMethod = "foo"; } + void bar() @safe { calledMethod = "bar"; } + void baz() @safe { calledMethod = "baz"; } + } + + enum FooEnum { foo, bar, baz } + + auto var = FooEnum.bar; + auto fooObj = new FooClass(); + s: final switch (var) + { + static foreach (member; EnumMembers!FooEnum) + { + case member: // Generate a case for each enum value. + // Call fooObj.{name of enum value}(). + __traits(getMember, fooObj, to!string(member))(); + break s; + } + } + // As we pass in FooEnum.bar, the bar() method gets called. + assert(fooObj.calledMethod == "bar"); +} + @safe unittest { enum A { a } @@ -3948,7 +4269,8 @@ template EnumMembers(E) static assert([ EnumMembers!A ] == [ A.a, A.b, A.c, A.d, A.e ]); } -@safe unittest // Bugzilla 14561: huge enums +// https://issues.dlang.org/show_bug.cgi?id=14561: huge enums +@safe unittest { string genEnum() { @@ -4002,6 +4324,8 @@ template BaseTypeTuple(A) /// @safe unittest { + import std.meta : AliasSeq; + interface I1 { } interface I2 { } interface I12 : I1, I2 { } @@ -4034,7 +4358,7 @@ template BaseTypeTuple(A) * BaseClassesTuple!Object) yields the empty type tuple. */ template BaseClassesTuple(T) - if (is(T == class)) +if (is(T == class)) { static if (is(T == Object)) { @@ -4044,6 +4368,10 @@ template BaseClassesTuple(T) { alias BaseClassesTuple = AliasSeq!Object; } + else static if (!is(BaseTypeTuple!T[0] == Object) && !is(BaseTypeTuple!T[0] == class)) + { + alias BaseClassesTuple = AliasSeq!(); + } else { alias BaseClassesTuple = @@ -4055,6 +4383,8 @@ template BaseClassesTuple(T) /// @safe unittest { + import std.meta : AliasSeq; + class C1 { } class C2 : C1 { } class C3 : C2 { } @@ -4064,6 +4394,22 @@ template BaseClassesTuple(T) static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object))); } +// https://issues.dlang.org/show_bug.cgi?id=17276 +@safe unittest +{ + extern (C++) static interface Ext + { + void someext(); + } + + extern (C++) static class E : Ext + { + void someext() {} + } + + alias BaseClassesWithNoObject = BaseClassesTuple!E; +} + @safe unittest { struct S { } @@ -4076,10 +4422,15 @@ template BaseClassesTuple(T) } /** - * Get a $(D_PARAM AliasSeq) of $(I all) interfaces directly or - * indirectly inherited by this class or interface. Interfaces do not - * repeat if multiply implemented. $(D_PARAM InterfacesTuple!Object) - * yields the empty type tuple. +Params: + T = The `class` or `interface` to search. + +Returns: + $(REF AliasSeq,std,meta) of all interfaces directly or + indirectly inherited by this class or interface. Interfaces + do not repeat if multiply implemented. + + `InterfacesTuple!Object` yields an empty `AliasSeq`. */ template InterfacesTuple(T) { @@ -4105,14 +4456,15 @@ template InterfacesTuple(T) alias InterfacesTuple = AliasSeq!(); } +/// @safe unittest { - // doc example interface I1 {} interface I2 {} - class A : I1, I2 { } - class B : A, I1 { } - class C : B { } + class A : I1, I2 {} + class B : A, I1 {} + class C : B {} + alias TL = InterfacesTuple!C; static assert(is(TL[0] == I1) && is(TL[1] == I2)); } @@ -4172,12 +4524,12 @@ template TransitiveBaseTypeTuple(T) /** -Returns a tuple of non-static functions with the name $(D name) declared in the -class or interface $(D C). Covariant duplicates are shrunk into the most +Returns a tuple of non-static functions with the name `name` declared in the +class or interface `C`. Covariant duplicates are shrunk into the most derived one. */ template MemberFunctionsTuple(C, string name) - if (is(C == class) || is(C == interface)) +if (is(C == class) || is(C == interface)) { static if (__traits(hasMember, C, name)) { @@ -4189,7 +4541,7 @@ template MemberFunctionsTuple(C, string name) static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name))) { // Get all overloads in sight (not hidden). - alias inSight = AliasSeq!(__traits(getVirtualFunctions, Node, name)); + alias inSight = __traits(getVirtualFunctions, Node, name); // And collect all overloads in ancestor classes to reveal hidden // methods. The result may contain duplicates. @@ -4213,8 +4565,11 @@ template MemberFunctionsTuple(C, string name) alias CollectOverloads = AliasSeq!(); // no overloads in this hierarchy } - // duplicates in this tuple will be removed by shrink() - alias overloads = CollectOverloads!C; + static if (name == "__ctor" || name == "__dtor") + alias overloads = AliasSeq!(__traits(getOverloads, C, name)); + else + // duplicates in this tuple will be removed by shrink() + alias overloads = CollectOverloads!C; // shrinkOne!args[0] = the most derived one in the covariant siblings of target // shrinkOne!args[1..$] = non-covariant others @@ -4232,7 +4587,7 @@ template MemberFunctionsTuple(C, string name) static if (isCovariantWith!(Target, Rest0) && isCovariantWith!(Rest0, Target)) { // One of these overrides the other. Choose the one from the most derived parent. - static if (is(AliasSeq!(__traits(parent, target))[0] : AliasSeq!(__traits(parent, rest[0]))[0])) + static if (is(__traits(parent, target) : __traits(parent, rest[0]))) alias shrinkOne = shrinkOne!(target, rest[1 .. $]); else alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); @@ -4293,7 +4648,8 @@ template MemberFunctionsTuple(C, string name) static assert(__traits(isSame, foos[1], B.foo)); } -@safe unittest // Issue 15920 +// https://issues.dlang.org/show_bug.cgi?id=15920 +@safe unittest { import std.meta : AliasSeq; class A @@ -4307,11 +4663,46 @@ template MemberFunctionsTuple(C, string name) override void f(int){} } alias fs = MemberFunctionsTuple!(B, "f"); - alias bfs = AliasSeq!(__traits(getOverloads, B, "f")); + alias bfs = __traits(getOverloads, B, "f"); assert(__traits(isSame, fs[0], bfs[0]) || __traits(isSame, fs[0], bfs[1])); assert(__traits(isSame, fs[1], bfs[0]) || __traits(isSame, fs[1], bfs[1])); } +// https://issues.dlang.org/show_bug.cgi?id=8388 +@safe unittest +{ + class C + { + this() {} + this(int i) {} + this(int i, float j) {} + this(string s) {} + + /* + Commented out, because this causes a cyclic dependency + between module constructors/destructors error. Might + be caused by https://issues.dlang.org/show_bug.cgi?id=20529. */ + // static this() {} + + ~this() {} + } + + class D : C + { + this() {} + ~this() {} + } + + alias test_ctor = MemberFunctionsTuple!(C, "__ctor"); + assert(test_ctor.length == 4); + alias test_dtor = MemberFunctionsTuple!(C, "__dtor"); + assert(test_dtor.length == 1); + alias test2_ctor = MemberFunctionsTuple!(D, "__ctor"); + assert(test2_ctor.length == 1); + alias test2_dtor = MemberFunctionsTuple!(D, "__dtor"); + assert(test2_dtor.length == 1); +} + @safe unittest { interface I { I test(); } @@ -4354,7 +4745,8 @@ template MemberFunctionsTuple(C, string name) /** -Returns an alias to the template that $(D T) is an instance of. +Returns an alias to the template that `T` is an instance of. +It will return `void` if a symbol without a template is given. */ template TemplateOf(alias T : Base!Args, alias Base, Args...) { @@ -4367,6 +4759,12 @@ template TemplateOf(T : Base!Args, alias Base, Args...) alias TemplateOf = Base; } +/// ditto +template TemplateOf(T) +{ + alias TemplateOf = void; +} + /// @safe unittest { @@ -4397,9 +4795,15 @@ template TemplateOf(T : Base!Args, alias Base, Args...) static assert(__traits(isSame, TemplateOf!(Foo10!()), Foo10)); } +// https://issues.dlang.org/show_bug.cgi?id=18214 +@safe unittest +{ + static assert(is(TemplateOf!(int[]) == void)); + static assert(is(TemplateOf!bool == void)); +} /** -Returns a $(D AliasSeq) of the template arguments used to instantiate $(D T). +Returns a `AliasSeq` of the template arguments used to instantiate `T`. */ template TemplateArgsOf(alias T : Base!Args, alias Base, Args...) { @@ -4415,6 +4819,8 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...) /// @safe unittest { + import std.meta : AliasSeq; + struct Foo(T, U) {} static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real))); } @@ -4445,17 +4851,20 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...) } -private template maxAlignment(U...) if (isTypeTuple!U) +package template maxAlignment(U...) +if (isTypeTuple!U) { - import std.meta : staticMap; static if (U.length == 0) static assert(0); else static if (U.length == 1) enum maxAlignment = U[0].alignof; + else static if (U.length == 2) + enum maxAlignment = U[0].alignof > U[1].alignof ? U[0].alignof : U[1].alignof; else { - import std.algorithm.comparison : max; - enum maxAlignment = max(staticMap!(.maxAlignment, U)); + enum a = maxAlignment!(U[0 .. ($+1)/2]); + enum b = maxAlignment!(U[($+1)/2 .. $]); + enum maxAlignment = a > b ? a : b; } } @@ -4463,7 +4872,8 @@ private template maxAlignment(U...) if (isTypeTuple!U) /** Returns class instance alignment. */ -template classInstanceAlignment(T) if (is(T == class)) +template classInstanceAlignment(T) +if (is(T == class)) { alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof)); } @@ -4523,26 +4933,186 @@ template CommonType(T...) alias Y = CommonType!(int, char[], short); assert(is(Y == void)); } + +/// @safe unittest { static assert(is(CommonType!(3) == int)); static assert(is(CommonType!(double, 4, float) == double)); static assert(is(CommonType!(string, char[]) == const(char)[])); static assert(is(CommonType!(3, 3U) == uint)); + static assert(is(CommonType!(double, int) == double)); } /** - * Returns a tuple with all possible target types of an implicit - * conversion of a value of type $(D_PARAM T). - * - * Important note: - * - * The possible targets are computed more conservatively than the D - * 2.005 compiler does, eliminating all dangerous conversions. For - * example, $(D_PARAM ImplicitConversionTargets!double) does not - * include $(D_PARAM float). +Params: + T = The type to check + +Returns: + An $(REF AliasSeq,std,meta) with all possible target types of an implicit + conversion `T`. + + If `T` is a class derived from `Object`, the result of + $(LREF TransitiveBaseTypeTuple) is returned. + + If the type is not a built-in value type or a class derived from + `Object`, an empty $(REF AliasSeq,std,meta) is returned. + +See_Also: + $(LREF isImplicitlyConvertible) + */ +template AllImplicitConversionTargets(T) +{ + static if (is(T == bool)) + alias AllImplicitConversionTargets = + AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList, + float, double, real, char, wchar, dchar); + else static if (is(T == byte)) + alias AllImplicitConversionTargets = + AliasSeq!(ubyte, short, ushort, int, uint, long, ulong, CentTypeList, + float, double, real, char, wchar, dchar); + else static if (is(T == ubyte)) + alias AllImplicitConversionTargets = + AliasSeq!(byte, short, ushort, int, uint, long, ulong, CentTypeList, + float, double, real, char, wchar, dchar); + else static if (is(T == short)) + alias AllImplicitConversionTargets = + AliasSeq!(ushort, int, uint, long, ulong, CentTypeList, float, double, real); + else static if (is(T == ushort)) + alias AllImplicitConversionTargets = + AliasSeq!(short, int, uint, long, ulong, CentTypeList, float, double, real); + else static if (is(T == int)) + alias AllImplicitConversionTargets = + AliasSeq!(uint, long, ulong, CentTypeList, float, double, real); + else static if (is(T == uint)) + alias AllImplicitConversionTargets = + AliasSeq!(int, long, ulong, CentTypeList, float, double, real); + else static if (is(T == long)) + alias AllImplicitConversionTargets = AliasSeq!(ulong, float, double, real); + else static if (is(T == ulong)) + alias AllImplicitConversionTargets = AliasSeq!(long, float, double, real); + else static if (is(cent) && is(T == cent)) + alias AllImplicitConversionTargets = AliasSeq!(UnsignedCentTypeList, float, double, real); + else static if (is(ucent) && is(T == ucent)) + alias AllImplicitConversionTargets = AliasSeq!(SignedCentTypeList, float, double, real); + else static if (is(T == float)) + alias AllImplicitConversionTargets = AliasSeq!(double, real); + else static if (is(T == double)) + alias AllImplicitConversionTargets = AliasSeq!(float, real); + else static if (is(T == real)) + alias AllImplicitConversionTargets = AliasSeq!(float, double); + else static if (is(T == char)) + alias AllImplicitConversionTargets = + AliasSeq!(wchar, dchar, byte, ubyte, short, ushort, + int, uint, long, ulong, CentTypeList, float, double, real); + else static if (is(T == wchar)) + alias AllImplicitConversionTargets = + AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList, + float, double, real); + else static if (is(T == dchar)) + alias AllImplicitConversionTargets = + AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); + else static if (is(T : typeof(null))) + alias AllImplicitConversionTargets = AliasSeq!(typeof(null)); + else static if (is(T == class)) + alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!(T)); + else static if (is(T == interface)) + alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), InterfacesTuple!(T)); + else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) + { + static if (is(typeof(T.init[0]) == shared)) + alias AllImplicitConversionTargets = + AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]); + else + alias AllImplicitConversionTargets = + AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); + } + else static if (is(T : void*)) + alias AllImplicitConversionTargets = AliasSeq!(void*); + else + alias AllImplicitConversionTargets = AliasSeq!(); +} + +/// +@safe unittest +{ + import std.meta : AliasSeq; + + static assert(is(AllImplicitConversionTargets!(ulong) == AliasSeq!(long, float, double, real))); + static assert(is(AllImplicitConversionTargets!(int) == AliasSeq!(uint, long, ulong, float, double, real))); + static assert(is(AllImplicitConversionTargets!(float) == AliasSeq!(double, real))); + static assert(is(AllImplicitConversionTargets!(double) == AliasSeq!(float, real))); + + static assert(is(AllImplicitConversionTargets!(char) == AliasSeq!( + wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real + ))); + static assert(is(AllImplicitConversionTargets!(wchar) == AliasSeq!( + dchar, short, ushort, int, uint, long, ulong, float, double, real + ))); + static assert(is(AllImplicitConversionTargets!(dchar) == AliasSeq!( + int, uint, long, ulong, float, double, real + ))); + + static assert(is(AllImplicitConversionTargets!(string) == AliasSeq!(const(char)[]))); + static assert(is(AllImplicitConversionTargets!(void*) == AliasSeq!(void*))); + + interface A {} + interface B {} + class C : A, B {} + + static assert(is(AllImplicitConversionTargets!(C) == AliasSeq!(Object, A, B))); + static assert(is(AllImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B))); + static assert(is(AllImplicitConversionTargets!(immutable C) == AliasSeq!( + immutable Object, immutable A, immutable B + ))); + + interface I : A, B {} + + static assert(is(AllImplicitConversionTargets!(I) == AliasSeq!(A, B))); + static assert(is(AllImplicitConversionTargets!(const I) == AliasSeq!(const A, const B))); + static assert(is(AllImplicitConversionTargets!(immutable I) == AliasSeq!( + immutable A, immutable B + ))); +} + +@safe unittest +{ + static assert(is(AllImplicitConversionTargets!(double)[0] == float)); + static assert(is(AllImplicitConversionTargets!(double)[1] == real)); + static assert(is(AllImplicitConversionTargets!(string)[0] == const(char)[])); +} + + +/** +Params: + T = The type to check + +Warning: + This template is considered out-dated. It will be removed from + Phobos in 2.107.0. Please use $(LREF AllImplicitConversionTargets) instead. + +Returns: + An $(REF AliasSeq,std,meta) with all possible target types of an implicit + conversion `T`. + + If `T` is a class derived from `Object`, the result of + $(LREF TransitiveBaseTypeTuple) is returned. + + If the type is not a built-in value type or a class derived from + `Object`, an empty $(REF AliasSeq,std,meta) is returned. + +Note: + The possible targets are computed more conservatively than the + language allows, eliminating all dangerous conversions. For example, + `ImplicitConversionTargets!double` does not include `float`. + +See_Also: + $(LREF isImplicitlyConvertible) */ +// @@@DEPRECATED_[2.107.0]@@@ +deprecated("ImplicitConversionTargets has been deprecated in favour of AllImplicitConversionTargets " + ~ "and will be removed in 2.107.0") template ImplicitConversionTargets(T) { static if (is(T == bool)) @@ -4594,36 +5164,66 @@ template ImplicitConversionTargets(T) AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); else static if (is(T : typeof(null))) alias ImplicitConversionTargets = AliasSeq!(typeof(null)); - else static if (is(T : Object)) - alias ImplicitConversionTargets = TransitiveBaseTypeTuple!(T); + else static if (is(T == class)) + alias ImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!(T)); else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) - alias ImplicitConversionTargets = - AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); + { + static if (is(typeof(T.init[0]) == shared)) + alias ImplicitConversionTargets = + AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]); + else + alias ImplicitConversionTargets = + AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); + } else static if (is(T : void*)) alias ImplicitConversionTargets = AliasSeq!(void*); else alias ImplicitConversionTargets = AliasSeq!(); } -@safe unittest +deprecated @safe unittest +{ + import std.meta : AliasSeq; + + static assert(is(ImplicitConversionTargets!(ulong) == AliasSeq!(float, double, real))); + static assert(is(ImplicitConversionTargets!(int) == AliasSeq!(long, ulong, float, double, real))); + static assert(is(ImplicitConversionTargets!(float) == AliasSeq!(double, real))); + static assert(is(ImplicitConversionTargets!(double) == AliasSeq!(real))); + + static assert(is(ImplicitConversionTargets!(char) == AliasSeq!( + wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real + ))); + static assert(is(ImplicitConversionTargets!(wchar) == AliasSeq!( + dchar, short, ushort, int, uint, long, ulong, float, double, real + ))); + static assert(is(ImplicitConversionTargets!(dchar) == AliasSeq!( + int, uint, long, ulong, float, double, real + ))); + + static assert(is(ImplicitConversionTargets!(string) == AliasSeq!(const(char)[]))); + static assert(is(ImplicitConversionTargets!(void*) == AliasSeq!(void*))); + + interface A {} + interface B {} + class C : A, B {} + + static assert(is(ImplicitConversionTargets!(C) == AliasSeq!(Object, A, B))); + static assert(is(ImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B))); + static assert(is(ImplicitConversionTargets!(immutable C) == AliasSeq!( + immutable Object, immutable A, immutable B + ))); +} + +deprecated @safe unittest { static assert(is(ImplicitConversionTargets!(double)[0] == real)); static assert(is(ImplicitConversionTargets!(string)[0] == const(char)[])); } /** -Is $(D From) implicitly convertible to $(D To)? +Is `From` implicitly convertible to `To`? */ -template isImplicitlyConvertible(From, To) -{ - enum bool isImplicitlyConvertible = is(typeof({ - void fun(ref From v) - { - void gun(To) {} - gun(v); - } - })); -} +enum bool isImplicitlyConvertible(From, To) = is(From : To); /// @safe unittest @@ -4642,12 +5242,12 @@ template isImplicitlyConvertible(From, To) } /** -Returns $(D true) iff a value of type $(D Rhs) can be assigned to a variable of -type $(D Lhs). +Returns `true` iff a value of type `Rhs` can be assigned to a variable of +type `Lhs`. -$(D isAssignable) returns whether both an lvalue and rvalue can be assigned. +`isAssignable` returns whether both an lvalue and rvalue can be assigned. -If you omit $(D Rhs), $(D isAssignable) will check identity assignable of $(D Lhs). +If you omit `Rhs`, `isAssignable` will check identity assignable of `Lhs`. */ enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs); @@ -4666,11 +5266,17 @@ enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAss static assert(!isAssignable!(immutable int)); } -// ditto -private enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs); +/** +Returns `true` iff an rvalue of type `Rhs` can be assigned to a variable of +type `Lhs` +*/ +enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; }); -// ditto -private enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs); +/** +Returns `true` iff an lvalue of type `Rhs` can be assigned to a variable of +type `Lhs` +*/ +enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; }); @safe unittest { @@ -4706,20 +5312,37 @@ private enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lh static assert( isAssignable!(S4, immutable int)); struct S5 { @disable this(); @disable this(this); } - struct S6 { void opAssign(in ref S5); } - static assert(!isAssignable!(S6, S5)); - static assert(!isRvalueAssignable!(S6, S5)); - static assert( isLvalueAssignable!(S6, S5)); - static assert( isLvalueAssignable!(S6, immutable S5)); + // https://issues.dlang.org/show_bug.cgi?id=21210 + static assert(!isAssignable!S5); + + // `-preview=in` is enabled + static if (!is(typeof(mixin(q{(in ref int a) => a})))) + { + struct S6 { void opAssign(in S5); } + + static assert(isRvalueAssignable!(S6, S5)); + static assert(isLvalueAssignable!(S6, S5)); + static assert(isAssignable!(S6, S5)); + static assert(isAssignable!(S6, immutable S5)); + } + else + { + mixin(q{ struct S6 { void opAssign(in ref S5); } }); + + static assert(!isRvalueAssignable!(S6, S5)); + static assert( isLvalueAssignable!(S6, S5)); + static assert(!isAssignable!(S6, S5)); + static assert( isLvalueAssignable!(S6, immutable S5)); + } } // Equivalent with TypeStruct::isAssignable in compiler code. package template isBlitAssignable(T) { - static if (is(OriginalType!T U) && !is(T == U)) + static if (is(T == enum)) { - enum isBlitAssignable = isBlitAssignable!U; + enum isBlitAssignable = isBlitAssignable!(OriginalType!T); } else static if (isStaticArray!T && is(T == E[n], E, size_t n)) // Workaround for issue 11499 : isStaticArray!T should not be necessary. @@ -4835,7 +5458,7 @@ package template isBlitAssignable(T) /* -Works like $(D isImplicitlyConvertible), except this cares only about storage +Works like `isImplicitlyConvertible`, except this cares only about storage classes of the arguments. */ private template isStorageClassImplicitlyConvertible(From, To) @@ -4860,11 +5483,13 @@ private template isStorageClassImplicitlyConvertible(From, To) /** -Determines whether the function type $(D F) is covariant with $(D G), i.e., -functions of the type $(D F) can override ones of the type $(D G). +Determines whether the function type `F` is covariant with `G`, i.e., +functions of the type `F` can override ones of the type `G`. */ template isCovariantWith(F, G) - if (is(F == function) && is(G == function)) +if (is(F == function) && is(G == function) || + is(F == delegate) && is(G == delegate) || + isFunctionPointer!F && isFunctionPointer!G) { static if (is(F : G)) enum isCovariantWith = true; @@ -4926,7 +5551,8 @@ template isCovariantWith(F, G) } /* * Check for parameters: - * - require exact match for types (cf. bugzilla 3075) + * - require exact match for types + * (cf. https://issues.dlang.org/show_bug.cgi?id=3075) * - require exact match for in, out, ref and lazy * - overrider can add scope, but can't remove */ @@ -5010,6 +5636,15 @@ template isCovariantWith(F, G) static assert( isCovariantWith!(DerivA_1.test, DerivA_1.test)); static assert( isCovariantWith!(DerivA_2.test, DerivA_2.test)); + // function, function pointer and delegate + J function() derived_function; + I function() base_function; + J delegate() derived_delegate; + I delegate() base_delegate; + static assert(.isCovariantWith!(typeof(derived_function), typeof(base_function))); + static assert(.isCovariantWith!(typeof(*derived_function), typeof(*base_function))); + static assert(.isCovariantWith!(typeof(derived_delegate), typeof(base_delegate))); + // scope parameter interface BaseB { void test( int*, int*); } interface DerivB_1 : BaseB { override void test(scope int*, int*); } @@ -5058,31 +5693,35 @@ template isCovariantWith(F, G) private struct __InoutWorkaroundStruct{} /** -Creates an lvalue or rvalue of type $(D T) for $(D typeof(...)) and -$(D __traits(compiles, ...)) purposes. No actual value is returned. +Creates an lvalue or rvalue of type `T` for `typeof(...)` and +`__traits(compiles, ...)` purposes. No actual value is returned. + +Params: + T = The type to transform Note: Trying to use returned value will result in a "Symbol Undefined" error at link time. - -Example: ---- -// Note that `f` doesn't have to be implemented -// as is isn't called. -int f(int); -bool f(ref int); -static assert(is(typeof(f(rvalueOf!int)) == int)); -static assert(is(typeof(f(lvalueOf!int)) == bool)); - -int i = rvalueOf!int; // error, no actual value is returned ---- */ @property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); /// ditto @property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); -// Note: unittest can't be used as an example here as function overloads +// Note: can't put these unittests together as function overloads // aren't allowed inside functions. +/// +@system unittest +{ + static int f(int); + static assert(is(typeof(f(rvalueOf!int)) == int)); +} + +/// +@system unittest +{ + static bool f(ref int); + static assert(is(typeof(f(lvalueOf!int)) == bool)); +} @system unittest { @@ -5090,7 +5729,7 @@ int i = rvalueOf!int; // error, no actual value is returned static struct S { } int i; struct Nested { void f() { ++i; } } - foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object)) + static foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object)) { static assert(!__traits(compiles, needLvalue(rvalueOf!T))); static assert( __traits(compiles, needLvalue(lvalueOf!T))); @@ -5108,17 +5747,7 @@ int i = rvalueOf!int; // error, no actual value is returned // SomethingTypeOf //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// -private template AliasThisTypeOf(T) if (isAggregateType!T) -{ - alias members = AliasSeq!(__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"); -} +private alias AliasThisTypeOf(T) = typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])); /* */ @@ -5129,7 +5758,7 @@ template BooleanTypeOf(T) else alias X = OriginalType!T; - static if (is(Unqual!X == bool)) + static if (is(immutable X == immutable bool)) { alias BooleanTypeOf = X; } @@ -5140,15 +5769,15 @@ template BooleanTypeOf(T) @safe unittest { // unexpected failure, maybe dmd type-merging bug - foreach (T; AliasSeq!bool) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!bool) + static foreach (Q; TypeQualifierList) { static assert( is(Q!T == BooleanTypeOf!( Q!T ))); static assert( is(Q!T == BooleanTypeOf!( SubTypeOf!(Q!T) ))); } - foreach (T; AliasSeq!(void, NumericTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList)) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!(void, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) + static foreach (Q; TypeQualifierList) { static assert(!is(BooleanTypeOf!( Q!T )), Q!T.stringof); static assert(!is(BooleanTypeOf!( SubTypeOf!(Q!T) ))); @@ -5175,13 +5804,13 @@ template BooleanTypeOf(T) */ template IntegralTypeOf(T) { - import std.meta : staticIndexOf; static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = IntegralTypeOf!AT; else alias X = OriginalType!T; - static if (staticIndexOf!(Unqual!X, IntegralTypeList) >= 0) + static if (__traits(isIntegral, X) && __traits(isZeroInit, X) // Not char, wchar, or dchar. + && !is(immutable X == immutable bool) && !is(X == __vector)) { alias IntegralTypeOf = X; } @@ -5191,15 +5820,16 @@ template IntegralTypeOf(T) @safe unittest { - foreach (T; IntegralTypeList) - foreach (Q; TypeQualifierList) + static foreach (T; IntegralTypeList) + static foreach (Q; TypeQualifierList) { static assert( is(Q!T == IntegralTypeOf!( Q!T ))); static assert( is(Q!T == IntegralTypeOf!( SubTypeOf!(Q!T) ))); } - foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList)) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, + /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) + static foreach (Q; TypeQualifierList) { static assert(!is(IntegralTypeOf!( Q!T ))); static assert(!is(IntegralTypeOf!( SubTypeOf!(Q!T) ))); @@ -5210,13 +5840,12 @@ template IntegralTypeOf(T) */ template FloatingPointTypeOf(T) { - import std.meta : staticIndexOf; static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = FloatingPointTypeOf!AT; else alias X = OriginalType!T; - static if (staticIndexOf!(Unqual!X, FloatingPointTypeList) >= 0) + static if (is(immutable X == immutable U, U) && is(U == float) || is(U == double) || is(U == real)) { alias FloatingPointTypeOf = X; } @@ -5226,15 +5855,15 @@ template FloatingPointTypeOf(T) @safe unittest { - foreach (T; FloatingPointTypeList) - foreach (Q; TypeQualifierList) + static foreach (T; FloatingPointTypeList) + static foreach (Q; TypeQualifierList) { static assert( is(Q!T == FloatingPointTypeOf!( Q!T ))); static assert( is(Q!T == FloatingPointTypeOf!( SubTypeOf!(Q!T) ))); } - foreach (T; AliasSeq!(void, bool, IntegralTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList)) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!(void, bool, IntegralTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) + static foreach (Q; TypeQualifierList) { static assert(!is(FloatingPointTypeOf!( Q!T ))); static assert(!is(FloatingPointTypeOf!( SubTypeOf!(Q!T) ))); @@ -5255,15 +5884,15 @@ template NumericTypeOf(T) @safe unittest { - foreach (T; NumericTypeList) - foreach (Q; TypeQualifierList) + static foreach (T; NumericTypeList) + static foreach (Q; TypeQualifierList) { static assert( is(Q!T == NumericTypeOf!( Q!T ))); static assert( is(Q!T == NumericTypeOf!( SubTypeOf!(Q!T) ))); } - foreach (T; AliasSeq!(void, bool, CharTypeList, ImaginaryTypeList, ComplexTypeList)) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!(void, bool, CharTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) + static foreach (Q; TypeQualifierList) { static assert(!is(NumericTypeOf!( Q!T ))); static assert(!is(NumericTypeOf!( SubTypeOf!(Q!T) ))); @@ -5274,9 +5903,7 @@ template NumericTypeOf(T) */ template UnsignedTypeOf(T) { - import std.meta : staticIndexOf; - static if (is(IntegralTypeOf!T X) && - staticIndexOf!(Unqual!X, UnsignedIntTypeList) >= 0) + static if (is(IntegralTypeOf!T X) && __traits(isUnsigned, X)) alias UnsignedTypeOf = X; else static assert(0, T.stringof~" is not an unsigned type."); @@ -5286,9 +5913,7 @@ template UnsignedTypeOf(T) */ template SignedTypeOf(T) { - import std.meta : staticIndexOf; - static if (is(IntegralTypeOf!T X) && - staticIndexOf!(Unqual!X, SignedIntTypeList) >= 0) + static if (is(IntegralTypeOf!T X) && !__traits(isUnsigned, X)) alias SignedTypeOf = X; else static if (is(FloatingPointTypeOf!T X)) alias SignedTypeOf = X; @@ -5300,13 +5925,12 @@ template SignedTypeOf(T) */ template CharTypeOf(T) { - import std.meta : staticIndexOf; static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = CharTypeOf!AT; else alias X = OriginalType!T; - static if (staticIndexOf!(Unqual!X, CharTypeList) >= 0) + static if (is(immutable X == immutable U, U) && is(U == char) || is(U == wchar) || is(U == dchar)) { alias CharTypeOf = X; } @@ -5316,22 +5940,22 @@ template CharTypeOf(T) @safe unittest { - foreach (T; CharTypeList) - foreach (Q; TypeQualifierList) + static foreach (T; CharTypeList) + static foreach (Q; TypeQualifierList) { static assert( is(CharTypeOf!( Q!T ))); static assert( is(CharTypeOf!( SubTypeOf!(Q!T) ))); } - foreach (T; AliasSeq!(void, bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList)) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!(void, bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) + static foreach (Q; TypeQualifierList) { static assert(!is(CharTypeOf!( Q!T ))); static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) ))); } - foreach (T; AliasSeq!(string, wstring, dstring, char[4])) - foreach (Q; TypeQualifierList) + static foreach (T; AliasSeq!(string, wstring, dstring, char[4])) + static foreach (Q; TypeQualifierList) { static assert(!is(CharTypeOf!( Q!T ))); static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) ))); @@ -5347,7 +5971,7 @@ template StaticArrayTypeOf(T) else alias X = OriginalType!T; - static if (is(X : E[n], E, size_t n)) + static if (__traits(isStaticArray, X)) alias StaticArrayTypeOf = X; else static assert(0, T.stringof~" is not a static array type"); @@ -5355,19 +5979,19 @@ template StaticArrayTypeOf(T) @safe unittest { - foreach (T; AliasSeq!(bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList)) - foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) + static foreach (T; AliasSeq!(bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) + static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) { static assert(is( Q!( T[1] ) == StaticArrayTypeOf!( Q!( T[1] ) ) )); - foreach (P; TypeQualifierList) + static foreach (P; TypeQualifierList) { // SubTypeOf cannot have inout type static assert(is( Q!(P!(T[1])) == StaticArrayTypeOf!( Q!(SubTypeOf!(P!(T[1]))) ) )); } } - foreach (T; AliasSeq!void) - foreach (Q; AliasSeq!TypeQualifierList) + static foreach (T; AliasSeq!void) + static foreach (Q; AliasSeq!TypeQualifierList) { static assert(is( StaticArrayTypeOf!( Q!(void[1]) ) == Q!(void[1]) )); } @@ -5382,7 +6006,7 @@ template DynamicArrayTypeOf(T) else alias X = OriginalType!T; - static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; }))) + static if (is(X == E[], E)) { alias DynamicArrayTypeOf = X; } @@ -5392,13 +6016,14 @@ template DynamicArrayTypeOf(T) @safe unittest { - foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList)) - foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) + import std.meta : Alias; + static foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) + static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) { static assert(is( Q!T[] == DynamicArrayTypeOf!( Q!T[] ) )); static assert(is( Q!(T[]) == DynamicArrayTypeOf!( Q!(T[]) ) )); - foreach (P; AliasSeq!(MutableOf, ConstOf, ImmutableOf)) + static foreach (P; AliasSeq!(Alias, ConstOf, ImmutableOf)) { static assert(is( Q!(P!T[]) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!T[])) ) )); static assert(is( Q!(P!(T[])) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!(T[]))) ) )); @@ -5423,7 +6048,20 @@ template ArrayTypeOf(T) } /* -Always returns the Dynamic Array version. + * Converts strings and string-like types to the corresponding dynamic array of characters. + * Params: + * T = one of the following: + * 1. dynamic arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const` + * (`shared` is rejected) + * 2. static arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const` + * (`shared` is rejected) + * 3. aggregates that use `alias this` to refer to a field that is (1), (2), or (3) + * + * Other cases are rejected with a compile time error. + * `typeof(null)` is rejected. + * + * Returns: + * The result of `[]` applied to the qualified character type. */ template StringTypeOf(T) { @@ -5447,23 +6085,24 @@ template StringTypeOf(T) @safe unittest { - foreach (T; CharTypeList) - foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf, InoutOf)) + import std.meta : Alias; + static foreach (T; CharTypeList) + static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, InoutOf)) { static assert(is(Q!T[] == StringTypeOf!( Q!T[] ))); static if (!__traits(isSame, Q, InoutOf)) - { + {{ static assert(is(Q!T[] == StringTypeOf!( SubTypeOf!(Q!T[]) ))); alias Str = Q!T[]; class C(S) { S val; alias val this; } static assert(is(StringTypeOf!(C!Str) == Str)); - } + }} } - foreach (T; CharTypeList) - foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf)) + static foreach (T; CharTypeList) + static foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf)) { static assert(!is(StringTypeOf!( Q!T[] ))); } @@ -5472,6 +6111,21 @@ template StringTypeOf(T) @safe unittest { static assert(is(StringTypeOf!(char[4]) == char[])); + + struct S + { + string s; + alias s this; + } + + struct T + { + S s; + alias s this; + } + + static assert(is(StringTypeOf!S == string)); + static assert(is(StringTypeOf!T == string)); } /* @@ -5483,7 +6137,7 @@ template AssocArrayTypeOf(T) else alias X = OriginalType!T; - static if (is(Unqual!X : V[K], K, V)) + static if (__traits(isAssociativeArray, X)) { alias AssocArrayTypeOf = X; } @@ -5493,19 +6147,19 @@ template AssocArrayTypeOf(T) @safe unittest { - foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) - foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) + static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) + static foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) + static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) + static foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) { static assert(is( P!(Q!T[R!T]) == AssocArrayTypeOf!( P!(Q!T[R!T]) ) )); } - foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) - foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) - foreach (P; AliasSeq!TypeQualifierList) - foreach (Q; AliasSeq!TypeQualifierList) - foreach (R; AliasSeq!TypeQualifierList) + static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) + static foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) + static foreach (P; AliasSeq!TypeQualifierList) + static foreach (Q; AliasSeq!TypeQualifierList) + static foreach (R; AliasSeq!TypeQualifierList) { static assert(is( O!(P!(Q!T[R!T])) == AssocArrayTypeOf!( O!(SubTypeOf!(P!(Q!T[R!T]))) ) )); } @@ -5515,16 +6169,21 @@ template AssocArrayTypeOf(T) */ template BuiltinTypeOf(T) { - static if (is(T : void)) alias BuiltinTypeOf = void; - else static if (is(BooleanTypeOf!T X)) alias BuiltinTypeOf = X; - else static if (is(IntegralTypeOf!T X)) alias BuiltinTypeOf = X; - else static if (is(FloatingPointTypeOf!T X))alias BuiltinTypeOf = X; - else static if (is(T : const(ireal))) alias BuiltinTypeOf = ireal; //TODO - else static if (is(T : const(creal))) alias BuiltinTypeOf = creal; //TODO - else static if (is(CharTypeOf!T X)) alias BuiltinTypeOf = X; - else static if (is(ArrayTypeOf!T X)) alias BuiltinTypeOf = X; - else static if (is(AssocArrayTypeOf!T X)) alias BuiltinTypeOf = X; - else static assert(0); + static if (is(T : void)) + alias BuiltinTypeOf = void; + else + { + static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) + alias X = BuiltinTypeOf!AT; + else + alias X = OriginalType!T; + static if (__traits(isArithmetic, X) && !is(X == __vector) || + __traits(isStaticArray, X) || is(X == E[], E) || + __traits(isAssociativeArray, X)) + alias BuiltinTypeOf = X; + else + static assert(0); + } } //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// @@ -5532,9 +6191,9 @@ template BuiltinTypeOf(T) //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// /** - * Detect whether $(D T) is a built-in boolean type. + * Detect whether `T` is a built-in boolean type. */ -enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T; +enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); /// @safe unittest @@ -5556,10 +6215,19 @@ enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T; } /** - * Detect whether $(D T) is a built-in integral type. Types $(D bool), - * $(D char), $(D wchar), and $(D dchar) are not considered integral. + * Detect whether `T` is a built-in integral type. Types `bool`, + * `char`, `wchar`, and `dchar` are not considered integral. */ -enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T; +template isIntegral(T) +{ + static if (!__traits(isIntegral, T)) + enum isIntegral = false; + else static if (is(T U == enum)) + enum isIntegral = isIntegral!U; + else + enum isIntegral = __traits(isZeroInit, T) // Not char, wchar, or dchar. + && !is(immutable T == immutable bool) && !is(T == __vector); +} /// @safe unittest @@ -5591,9 +6259,9 @@ enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T; @safe unittest { - foreach (T; IntegralTypeList) + static foreach (T; IntegralTypeList) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isIntegral!(Q!T)); static assert(!isIntegral!(SubTypeOf!(Q!T))); @@ -5603,20 +6271,16 @@ enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T; static assert(!isIntegral!float); enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned - enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909) + // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909) + enum EI : int { a = -1, b = 0, c = 1 } static assert(isIntegral!EU && isUnsigned!EU && !isSigned!EU); static assert(isIntegral!EI && !isUnsigned!EI && isSigned!EI); } /** - * Detect whether $(D T) is a built-in floating point type. + * Detect whether `T` is a built-in floating point type. */ -enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloat) || - is(Unqual!T == cdouble) || - is(Unqual!T == creal) || - is(Unqual!T == ifloat) || - is(Unqual!T == idouble) || - is(Unqual!T == ireal)); +enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real); /// @safe unittest @@ -5631,12 +6295,6 @@ enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloa static assert(!isFloatingPoint!int); - // complex and imaginary numbers do not pass - static assert( - !isFloatingPoint!cfloat && - !isFloatingPoint!ifloat - ); - // types which act as floating point values do not pass struct S { @@ -5651,43 +6309,43 @@ enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloa { enum EF : real { a = 1.414, b = 1.732, c = 2.236 } - foreach (T; AliasSeq!(FloatingPointTypeList, EF)) + static foreach (T; AliasSeq!(FloatingPointTypeList, EF)) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isFloatingPoint!(Q!T)); static assert(!isFloatingPoint!(SubTypeOf!(Q!T))); } } - foreach (T; IntegralTypeList) + static foreach (T; IntegralTypeList) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert(!isFloatingPoint!(Q!T)); } } -} - -// https://issues.dlang.org/show_bug.cgi?id=17195 -@safe unittest -{ - static assert(!isFloatingPoint!cfloat); - static assert(!isFloatingPoint!cdouble); - static assert(!isFloatingPoint!creal); - - static assert(!isFloatingPoint!ifloat); - static assert(!isFloatingPoint!idouble); - static assert(!isFloatingPoint!ireal); + static if (is(__vector(float[4]))) + { + static assert(!isFloatingPoint!(__vector(float[4]))); + } } /** - * Detect whether $(D T) is a built-in numeric type (integral or floating + * Detect whether `T` is a built-in numeric type (integral or floating * point). */ -enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) || - is(Unqual!T == char) || - is(Unqual!T == wchar) || - is(Unqual!T == dchar)); +template isNumeric(T) +{ + static if (!__traits(isArithmetic, T)) + enum isNumeric = false; + else static if (__traits(isFloating, T)) + enum isNumeric = is(T : real); // Not __vector, imaginary, or complex. + else static if (is(T U == enum)) + enum isNumeric = isNumeric!U; + else + enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar. + && !is(immutable T == immutable bool) && !is(T == __vector); +} /// @safe unittest @@ -5719,14 +6377,14 @@ enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) || alias val this; } - static assert(!isIntegral!S); + static assert(!isNumeric!S); } @safe unittest { - foreach (T; AliasSeq!(NumericTypeList)) + static foreach (T; AliasSeq!(NumericTypeList)) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isNumeric!(Q!T)); static assert(!isNumeric!(SubTypeOf!(Q!T))); @@ -5739,13 +6397,28 @@ enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) || alias t this; } static assert(!isNumeric!(S!int)); + + enum EChar : char { a = 0, } + static assert(!isNumeric!EChar); + + static if (is(__vector(float[4]))) + { + static assert(!isNumeric!(__vector(float[4]))); + } + static if (is(__vector(int[4]))) + { + static assert(!isNumeric!(__vector(int[4]))); + } + + static assert(!isNumeric!ifloat); + static assert(!isNumeric!cfloat); } /** - * Detect whether $(D T) is a scalar type (a built-in numeric, character or + * Detect whether `T` is a scalar type (a built-in numeric, character or * boolean type). */ -enum bool isScalarType(T) = is(T : real) && !isAggregateType!T; +enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real); /// @safe unittest @@ -5775,9 +6448,9 @@ enum bool isScalarType(T) = is(T : real) && !isAggregateType!T; } /** - * Detect whether $(D T) is a basic type (scalar type or void). + * Detect whether `T` is a basic type (scalar type or void). */ -enum bool isBasicType(T) = isScalarType!T || is(Unqual!T == void); +enum bool isBasicType(T) = isScalarType!T || is(immutable T == immutable void); /// @safe unittest @@ -5798,12 +6471,18 @@ enum bool isBasicType(T) = isScalarType!T || is(Unqual!T == void); } /** - * Detect whether $(D T) is a built-in unsigned numeric type. + * Detect whether `T` is a built-in unsigned numeric type. */ -enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) || - is(Unqual!T == wchar) || - is(Unqual!T == dchar) || - is(Unqual!T == bool)); +template isUnsigned(T) +{ + static if (!__traits(isUnsigned, T)) + enum isUnsigned = false; + else static if (is(T U == enum)) + enum isUnsigned = isUnsigned!U; + else + enum isUnsigned = __traits(isZeroInit, T) // Not char, wchar, or dchar. + && !is(immutable T == immutable bool) && !is(T == __vector); +} /// @safe unittest @@ -5825,9 +6504,9 @@ enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) || @safe unittest { - foreach (T; AliasSeq!(UnsignedIntTypeList)) + static foreach (T; AliasSeq!(UnsignedIntTypeList)) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isUnsigned!(Q!T)); static assert(!isUnsigned!(SubTypeOf!(Q!T))); @@ -5840,12 +6519,21 @@ enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) || alias t this; } static assert(!isUnsigned!(S!uint)); + + enum EChar : char { a = 0, } + static assert(!isUnsigned!EChar); + + static if (is(__vector(uint[4]))) + { + static assert(!isUnsigned!(__vector(uint[4]))); + } } /** - * Detect whether $(D T) is a built-in signed numeric type. + * Detect whether `T` is a built-in signed numeric type. */ -enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T); +enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) + && is(T : real); /// @safe unittest @@ -5869,9 +6557,9 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T); enum Eubyte : ubyte { e1 = 0 } static assert(!isSigned!Eubyte); - foreach (T; AliasSeq!(SignedIntTypeList)) + static foreach (T; AliasSeq!(SignedIntTypeList)) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isSigned!(Q!T)); static assert(!isSigned!(SubTypeOf!(Q!T))); @@ -5884,6 +6572,14 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T); alias t this; } static assert(!isSigned!(S!uint)); + + static if (is(__vector(int[4]))) + { + static assert(!isSigned!(__vector(int[4]))); + } + + static assert(!isSigned!ifloat); + static assert(!isSigned!cfloat); } // https://issues.dlang.org/show_bug.cgi?id=17196 @@ -5894,12 +6590,20 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T); } /** - * Detect whether $(D T) is one of the built-in character types. + * Detect whether `T` is one of the built-in character types. * - * The built-in char types are any of $(D char), $(D wchar) or $(D dchar), with + * The built-in char types are any of `char`, `wchar` or `dchar`, with * or without qualifiers. */ -enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T; +template isSomeChar(T) +{ + static if (!__traits(isUnsigned, T)) + enum isSomeChar = false; + else static if (is(T U == enum)) + enum isSomeChar = isSomeChar!U; + else + enum isSomeChar = !__traits(isZeroInit, T); +} /// @safe unittest @@ -5925,9 +6629,9 @@ enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T; { enum EC : char { a = 'x', b = 'y' } - foreach (T; AliasSeq!(CharTypeList, EC)) + static foreach (T; AliasSeq!(CharTypeList, EC)) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isSomeChar!( Q!T )); static assert(!isSomeChar!( SubTypeOf!(Q!T) )); @@ -5944,15 +6648,15 @@ enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T; } /** -Detect whether $(D T) is one of the built-in string types. +Detect whether `T` is one of the built-in string types. -The built-in string types are $(D Char[]), where $(D Char) is any of $(D char), -$(D wchar) or $(D dchar), with or without qualifiers. +The built-in string types are `Char[]`, where `Char` is any of `char`, +`wchar` or `dchar`, with or without qualifiers. -Static arrays of characters (like $(D char[80])) are not considered +Static arrays of characters (like `char[80]`) are not considered built-in string types. */ -enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStaticArray!T; +enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); /// @safe unittest @@ -5964,33 +6668,42 @@ enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStati static assert( isSomeString!(typeof("aaa"))); static assert( isSomeString!(const(char)[])); - enum ES : string { a = "aaa", b = "bbb" } - static assert( isSomeString!ES); - //Non string types static assert(!isSomeString!int); static assert(!isSomeString!(int[])); static assert(!isSomeString!(byte[])); static assert(!isSomeString!(typeof(null))); static assert(!isSomeString!(char[4])); + + enum ES : string { a = "aaa", b = "bbb" } + static assert(!isSomeString!ES); + + static struct Stringish + { + string str; + alias str this; + } + static assert(!isSomeString!Stringish); } @safe unittest { - foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring)) + static foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring)) { static assert( isSomeString!( T )); static assert(!isSomeString!(SubTypeOf!(T))); } + enum C : char { _ = 0 } + static assert(!isSomeString!(C[])); } /** - * Detect whether type $(D T) is a narrow string. + * Detect whether type `T` is a narrow string. * * All arrays that use char, wchar, and their qualified versions are narrow * strings. (Those include string and wstring). */ -enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isAggregateType!T && !isStaticArray!T; +enum bool isNarrowString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar)); /// @safe unittest @@ -6002,41 +6715,56 @@ enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) && static assert(!isNarrowString!dstring); static assert(!isNarrowString!(dchar[])); + + static assert(!isNarrowString!(typeof(null))); + static assert(!isNarrowString!(char[4])); + + enum ES : string { a = "aaa", b = "bbb" } + static assert(!isNarrowString!ES); + + static struct Stringish + { + string str; + alias str this; + } + static assert(!isNarrowString!Stringish); } @safe unittest { - foreach (T; AliasSeq!(char[], string, wstring)) + import std.meta : Alias; + static foreach (T; AliasSeq!(char[], string, wstring)) { - foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf)/*TypeQualifierList*/) + static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf)/*TypeQualifierList*/) { static assert( isNarrowString!( Q!T )); static assert(!isNarrowString!( SubTypeOf!(Q!T) )); } } - foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4])) + static foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4])) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert(!isNarrowString!( Q!T )); static assert(!isNarrowString!( SubTypeOf!(Q!T) )); } } + enum C : char { _ = 0 } + static assert(!isNarrowString!(C[])); } /** * Detects whether `T` is a comparable type. Basic types and structs and * classes that implement opCmp are ordering comparable. */ -enum bool isOrderingComparable(T) = ifTestable!(T, unaryFun!"a < a"); +enum bool isOrderingComparable(T) = is(typeof((ref T a) => a < a ? 1 : 0)); /// @safe unittest { static assert(isOrderingComparable!int); static assert(isOrderingComparable!string); - static assert(!isOrderingComparable!creal); static struct Foo {} static assert(!isOrderingComparable!Foo); @@ -6053,13 +6781,12 @@ enum bool isOrderingComparable(T) = ifTestable!(T, unaryFun!"a < a"); } /// ditto -enum bool isEqualityComparable(T) = ifTestable!(T, unaryFun!"a == a"); +enum bool isEqualityComparable(T) = is(typeof((ref T a) => a == a ? 1 : 0)); @safe unittest { static assert(isEqualityComparable!int); static assert(isEqualityComparable!string); - static assert(isEqualityComparable!creal); static assert(!isEqualityComparable!void); struct Foo {} @@ -6080,8 +6807,27 @@ enum bool isEqualityComparable(T) = ifTestable!(T, unaryFun!"a == a"); } /** - * Detect whether $(D T) is a struct, static array, or enum that is implicitly - * convertible to a string. + $(RED Warning: This trait will be deprecated as soon as it is no longer used + in Phobos. For a function parameter to safely accept a type + that implicitly converts to string as a string, the conversion + needs to happen at the callsite; otherwise, the conversion is + done inside the function, and in many cases, that means that + local memory is sliced (e.g. if a static array is passed to + the function, then it's copied, and the resulting dynamic + array will be a slice of a local variable). So, if the + resulting string escapes the function, the string refers to + invalid memory, and accessing it would mean accessing invalid + memory. As such, the only safe way for a function to accept + types that implicitly convert to string is for the implicit + conversion to be done at the callsite, and that can only occur + if the parameter is explicitly typed as an array, whereas + using isConvertibleToString in a template constraint would + result in the conversion being done inside the function. As + such, isConvertibleToString is inherently unsafe and is going + to be deprecated.) + + Detect whether `T` is a struct, static array, or enum that is implicitly + convertible to a string. */ template isConvertibleToString(T) { @@ -6108,7 +6854,8 @@ template isConvertibleToString(T) assert(!isConvertibleToString!(char[])); } -@safe unittest // Bugzilla 16573 +// https://issues.dlang.org/show_bug.cgi?id=16573 +@safe unittest { enum I : int { foo = 1 } enum S : string { foo = "foo" } @@ -6125,11 +6872,21 @@ package template convertToString(T) } /** - * Detect whether type $(D T) is a string that will be autodecoded. + * Detect whether type `T` is a string that will be autodecoded. * - * All arrays that use char, wchar, and their qualified versions are narrow - * strings. (Those include string and wstring). - * Aggregates that implicitly cast to narrow strings are included. + * Given a type `S` that is one of: + * $(OL + * $(LI `const(char)[]`) + * $(LI `const(wchar)[]`) + * ) + * Type `T` can be one of: + * $(OL + * $(LI `S`) + * $(LI implicitly convertible to `T`) + * $(LI an enum with a base type `T`) + * $(LI an aggregate with a base type `T`) + * ) + * with the proviso that `T` cannot be a static array. * * Params: * T = type to be tested @@ -6140,7 +6897,13 @@ package template convertToString(T) * See Also: * $(LREF isNarrowString) */ -enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isStaticArray!T; +template isAutodecodableString(T) +{ + import std.range.primitives : autodecodeStrings; + + enum isAutodecodableString = autodecodeStrings && + (is(T : const char[]) || is(T : const wchar[])) && !is(T : U[n], U, size_t n); +} /// @safe unittest @@ -6150,13 +6913,34 @@ enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar string s; alias s this; } - assert(isAutodecodableString!wstring); - assert(isAutodecodableString!Stringish); - assert(!isAutodecodableString!dstring); + static assert(isAutodecodableString!wstring); + static assert(isAutodecodableString!Stringish); + static assert(!isAutodecodableString!dstring); + + enum E : const(char)[3] { X = "abc" } + enum F : const(char)[] { X = "abc" } + enum G : F { X = F.init } + + static assert(isAutodecodableString!(char[])); + static assert(!isAutodecodableString!(E)); + static assert(isAutodecodableString!(F)); + static assert(isAutodecodableString!(G)); + + struct Stringish2 + { + Stringish s; + alias s this; + } + + enum H : Stringish { X = Stringish() } + enum I : Stringish2 { X = Stringish2() } + + static assert(isAutodecodableString!(H)); + static assert(isAutodecodableString!(I)); } /** - * Detect whether type $(D T) is a static array. + * Detect whether type `T` is a static array. */ enum bool isStaticArray(T) = __traits(isStaticArray, T); @@ -6179,11 +6963,11 @@ enum bool isStaticArray(T) = __traits(isStaticArray, T); @safe unittest { - foreach (T; AliasSeq!(int[51], int[][2], + static foreach (T; AliasSeq!(int[51], int[][2], char[][int][11], immutable char[13u], const(real)[1], const(real)[1][1], void[0])) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isStaticArray!( Q!T )); static assert(!isStaticArray!( SubTypeOf!(Q!T) )); @@ -6195,9 +6979,21 @@ enum bool isStaticArray(T) = __traits(isStaticArray, T); } /** - * Detect whether type $(D T) is a dynamic array. + * Detect whether type `T` is a dynamic array. */ -enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; +template isDynamicArray(T) +{ + static if (is(T == U[], U)) + enum bool isDynamicArray = true; + else static if (is(T U == enum)) + // BUG: isDynamicArray / isStaticArray considers enums + // with appropriate base types as dynamic/static arrays + // Retain old behaviour for now, see + // https://github.com/dlang/phobos/pull/7574 + enum bool isDynamicArray = isDynamicArray!U; + else + enum bool isDynamicArray = false; +} /// @safe unittest @@ -6213,18 +7009,36 @@ enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; @safe unittest { import std.meta : AliasSeq; - foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) + static foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isDynamicArray!( Q!T )); static assert(!isDynamicArray!( SubTypeOf!(Q!T) )); } } + + static assert(!isDynamicArray!(int[5])); + + static struct AliasThis + { + int[] values; + alias values this; + } + + static assert(!isDynamicArray!AliasThis); + + // https://github.com/dlang/phobos/pull/7574/files#r464115492 + enum E : string + { + a = "a", + b = "b", + } + static assert( isDynamicArray!E); } /** - * Detect whether type $(D T) is an array (static or dynamic; for associative + * Detect whether type `T` is an array (static or dynamic; for associative * arrays see $(LREF isAssociativeArray)). */ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; @@ -6244,9 +7058,9 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; @safe unittest { import std.meta : AliasSeq; - foreach (T; AliasSeq!(int[], int[5], void[])) + static foreach (T; AliasSeq!(int[], int[5], void[])) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isArray!(Q!T)); static assert(!isArray!(SubTypeOf!(Q!T))); @@ -6255,7 +7069,7 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; } /** - * Detect whether $(D T) is an associative array type + * Detect whether `T` is an associative array type */ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); @@ -6267,9 +7081,9 @@ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); @property uint[] values() { return null; } } - foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int])) + static foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int])) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isAssociativeArray!(Q!T)); static assert(!isAssociativeArray!(SubTypeOf!(Q!T))); @@ -6286,7 +7100,7 @@ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); } /** - * Detect whether type $(D T) is a builtin type. + * Detect whether type `T` is a builtin type. */ enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; @@ -6310,7 +7124,7 @@ enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; } /** - * Detect whether type $(D T) is a SIMD vector type. + * Detect whether type `T` is a SIMD vector type. */ enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); @@ -6327,15 +7141,15 @@ enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); } /** - * Detect whether type $(D T) is a pointer. + * Detect whether type `T` is a pointer. */ -enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T; +enum bool isPointer(T) = is(T == U*, U) && __traits(isScalar, T); @safe unittest { - foreach (T; AliasSeq!(int*, void*, char[]*)) + static foreach (T; AliasSeq!(int*, void*, char[]*)) { - foreach (Q; TypeQualifierList) + static foreach (Q; TypeQualifierList) { static assert( isPointer!(Q!T)); static assert(!isPointer!(SubTypeOf!(Q!T))); @@ -6361,7 +7175,7 @@ alias PointerTarget(T : T*) = T; } /** - * Detect whether type $(D T) is an aggregate type. + * Detect whether type `T` is an aggregate type. */ enum bool isAggregateType(T) = is(T == struct) || is(T == union) || is(T == class) || is(T == interface); @@ -6386,10 +7200,10 @@ enum bool isAggregateType(T) = is(T == struct) || is(T == union) || } /** - * Returns $(D true) if T can be iterated over using a $(D foreach) loop with + * Returns `true` if T can be iterated over using a `foreach` loop with * a single loop variable of automatically inferred type, regardless of how - * the $(D foreach) loop is implemented. This includes ranges, structs/classes - * that define $(D opApply) with a single loop variable, and builtin dynamic, + * the `foreach` loop is implemented. This includes ranges, structs/classes + * that define `opApply` with a single loop variable, and builtin dynamic, * static and associative arrays. */ enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} })); @@ -6470,6 +7284,30 @@ template isInstanceOf(alias S, alias T) static assert(isInstanceOf!(templ, templ!int)); } +/** + * To use `isInstanceOf` to check the identity of a template while inside of said + * template, use $(LREF TemplateOf). + */ +@safe unittest +{ + static struct A(T = void) + { + // doesn't work as expected, only accepts A when T = void + void func(B)(B b) if (isInstanceOf!(A, B)) {} + + // correct behavior + void method(B)(B b) if (isInstanceOf!(TemplateOf!(A), B)) {} + } + + A!(void) a1; + A!(void) a2; + A!(int) a3; + + static assert(!__traits(compiles, a1.func(a3))); + static assert( __traits(compiles, a1.method(a2))); + static assert( __traits(compiles, a1.method(a3))); +} + @safe unittest { static void fun1(T)() { } @@ -6487,17 +7325,20 @@ template isInstanceOf(alias S, alias T) * * See_Also: $(LREF isTypeTuple). */ -template isExpressions(T ...) +template isExpressions(T...) { - static if (T.length >= 2) - enum bool isExpressions = - isExpressions!(T[0 .. $/2]) && - isExpressions!(T[$/2 .. $]); - else static if (T.length == 1) - enum bool isExpressions = - !is(T[0]) && __traits(compiles, { auto ex = T[0]; }); - else - enum bool isExpressions = true; // default + static foreach (Ti; T) + { + static if (!is(typeof(isExpressions) == bool) && // not yet defined + (is(Ti) || !__traits(compiles, { auto ex = Ti; }))) + { + enum isExpressions = false; + } + } + static if (!is(typeof(isExpressions) == bool)) // if not yet defined + { + enum isExpressions = true; + } } /// @@ -6535,7 +7376,7 @@ alias isExpressionTuple = isExpressions; /** - * Check whether the tuple $(D T) is a type tuple. + * Check whether the tuple `T` is a type tuple. * A type tuple only contains types. * * See_Also: $(LREF isExpressions). @@ -6578,21 +7419,9 @@ template isTypeTuple(T...) /** -Detect whether symbol or type $(D T) is a function pointer. +Detect whether symbol or type `T` is a function pointer. */ -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; -} +enum bool isFunctionPointer(alias T) = is(typeof(*T) == function); /// @safe unittest @@ -6614,24 +7443,9 @@ template isFunctionPointer(T...) } /** -Detect whether symbol or type $(D T) is a delegate. +Detect whether symbol or type `T` is a delegate. */ -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; -} +enum bool isDelegate(alias T) = is(typeof(T) == delegate) || is(T == delegate); /// @safe unittest @@ -6652,10 +7466,15 @@ template isDelegate(T...) } /** -Detect whether symbol or type $(D T) is a function, a function pointer or a delegate. +Detect whether symbol or type `T` is a function, a function pointer or a delegate. + +Params: + T = The type to check +Returns: + A `bool` */ template isSomeFunction(T...) - if (T.length == 1) +if (T.length == 1) { static if (is(typeof(& T[0]) U : U*) && is(U == function) || is(typeof(& T[0]) U == delegate)) { @@ -6674,12 +7493,11 @@ template isSomeFunction(T...) enum bool isSomeFunction = false; } +/// @safe unittest { static real func(ref int) { return 0; } static void prop() @property { } - void nestedFunc() { } - void nestedProp() @property { } class C { real method(ref int) { return 0; } @@ -6692,69 +7510,126 @@ template isSomeFunction(T...) static assert( isSomeFunction!func); static assert( isSomeFunction!prop); - static assert( isSomeFunction!nestedFunc); - static assert( isSomeFunction!nestedProp); static assert( isSomeFunction!(C.method)); static assert( isSomeFunction!(C.prop)); static assert( isSomeFunction!(c.prop)); static assert( isSomeFunction!(c.prop)); static assert( isSomeFunction!fp); static assert( isSomeFunction!dg); - static assert( isSomeFunction!(typeof(func))); - static assert( isSomeFunction!(real function(ref int))); - static assert( isSomeFunction!(real delegate(ref int))); - static assert( isSomeFunction!((int a) { return a; })); static assert(!isSomeFunction!int); static assert(!isSomeFunction!val); - static assert(!isSomeFunction!isSomeFunction); } +@safe unittest +{ + void nestedFunc() { } + void nestedProp() @property { } + static assert(isSomeFunction!nestedFunc); + static assert(isSomeFunction!nestedProp); + static assert(isSomeFunction!(real function(ref int))); + static assert(isSomeFunction!(real delegate(ref int))); + static assert(isSomeFunction!((int a) { return a; })); + static assert(!isSomeFunction!isSomeFunction); +} /** -Detect whether $(D T) is a callable object, which can be called with the -function call operator $(D $(LPAREN)...$(RPAREN)). +Detect whether `T` is a callable object, which can be called with the +function call operator `$(LPAREN)...$(RPAREN)`. */ -template isCallable(T...) - if (T.length == 1) +template isCallable(alias callable) { - static if (is(typeof(& T[0].opCall) == delegate)) + static if (is(typeof(&callable.opCall) == delegate)) // T is a object which has a member function opCall(). enum bool isCallable = true; - else static if (is(typeof(& T[0].opCall) V : V*) && is(V == function)) + else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) // T is a type which has a static member function opCall(). enum bool isCallable = true; + else static if (is(typeof(&callable.opCall!()))) + { + alias TemplateInstanceType = typeof(&callable.opCall!()); + enum bool isCallable = isCallable!TemplateInstanceType; + } + else static if (is(typeof(&callable!()))) + { + alias TemplateInstanceType = typeof(&callable!()); + enum bool isCallable = isCallable!TemplateInstanceType; + } else - enum bool isCallable = isSomeFunction!T; + { + enum bool isCallable = isSomeFunction!callable; + } } -/// +/// Functions, lambdas, and aggregate types with (static) opCall. @safe unittest { - interface I { real value() @property; } - struct S { static int opCall(int) { return 0; } } + void f() { } + int g(int x) { return x; } + + static assert( isCallable!f); + static assert( isCallable!g); + class C { int opCall(int) { return 0; } } auto c = new C; + struct S { static int opCall(int) { return 0; } } + interface I { real value() @property; } static assert( isCallable!c); - static assert( isCallable!S); static assert( isCallable!(c.opCall)); + static assert( isCallable!S); static assert( isCallable!(I.value)); static assert( isCallable!((int a) { return a; })); static assert(!isCallable!I); } +/// Templates +@safe unittest +{ + void f()() { } + T g(T = int)(T x) { return x; } + struct S1 { static void opCall()() { } } + struct S2 { static T opCall(T = int)(T x) {return x; } } + + static assert( isCallable!f); + static assert( isCallable!g); + static assert( isCallable!S1); + static assert( isCallable!S2); +} + +/// Overloaded functions and function templates. +@safe unittest +{ + static struct Wrapper + { + void f() { } + int f(int x) { return x; } + + void g()() { } + T g(T = int)(T x) { return x; } + } + + static assert(isCallable!(Wrapper.f)); + static assert(isCallable!(Wrapper.g)); +} + /** - * Detect whether $(D T) is an abstract function. +Detect whether `T` is an abstract function. + +Params: + T = The type to check +Returns: + A `bool` */ template isAbstractFunction(T...) - if (T.length == 1) +if (T.length == 1) { enum bool isAbstractFunction = __traits(isAbstractFunction, T[0]); } +/// @safe unittest { struct S { void foo() { } } @@ -6767,10 +7642,10 @@ template isAbstractFunction(T...) } /** - * Detect whether $(D T) is a final function. + * Detect whether `T` is a final function. */ template isFinalFunction(T...) - if (T.length == 1) +if (T.length == 1) { enum bool isFinalFunction = __traits(isFinalFunction, T[0]); } @@ -6793,26 +7668,56 @@ template isFinalFunction(T...) } /** -Determines whether function $(D f) requires a context pointer. +Determines if `f` is a function that requires a context pointer. + +Params: + f = The type to check +Returns + A `bool` */ template isNestedFunction(alias f) { - enum isNestedFunction = __traits(isNested, f); + enum isNestedFunction = __traits(isNested, f) && isSomeFunction!(f); } +/// @safe unittest { - static void f() { } - void g() { } + static void f() {} + static void fun() + { + int i; + int f() { return i; } + + static assert(isNestedFunction!(f)); + } + static assert(!isNestedFunction!f); - static assert( isNestedFunction!g); +} + +// https://issues.dlang.org/show_bug.cgi?id=18669 +@safe unittest +{ + static class Outer + { + class Inner + { + } + } + int i; + struct SS + { + int bar() { return i; } + } + static assert(!isNestedFunction!(Outer.Inner)); + static assert(!isNestedFunction!(SS)); } /** - * Detect whether $(D T) is an abstract class. + * Detect whether `T` is an abstract class. */ template isAbstractClass(T...) - if (T.length == 1) +if (T.length == 1) { enum bool isAbstractClass = __traits(isAbstractClass, T[0]); } @@ -6833,10 +7738,10 @@ template isAbstractClass(T...) } /** - * Detect whether $(D T) is a final class. + * Detect whether `T` is a final class. */ template isFinalClass(T...) - if (T.length == 1) +if (T.length == 1) { enum bool isFinalClass = __traits(isFinalClass, T[0]); } @@ -6863,30 +7768,47 @@ template isFinalClass(T...) //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// /** -Removes all qualifiers, if any, from type $(D T). +Removes `const`, `inout` and `immutable` qualifiers, if any, from type `T`. + */ +template Unconst(T) +{ + import core.internal.traits : CoreUnconst = Unconst; + alias Unconst = CoreUnconst!(T); +} + +/// +@safe unittest +{ + static assert(is(Unconst!int == int)); + static assert(is(Unconst!(const int) == int)); + static assert(is(Unconst!(immutable int) == int)); + static assert(is(Unconst!(shared int) == shared int)); + static assert(is(Unconst!(shared(const int)) == shared int)); +} + +@safe unittest +{ + static assert(is(Unconst!( int) == int)); + static assert(is(Unconst!( const int) == int)); + static assert(is(Unconst!( inout int) == int)); + static assert(is(Unconst!( inout const int) == int)); + static assert(is(Unconst!(shared int) == shared int)); + static assert(is(Unconst!(shared const int) == shared int)); + static assert(is(Unconst!(shared inout int) == shared int)); + static assert(is(Unconst!(shared inout const int) == shared int)); + static assert(is(Unconst!( immutable int) == int)); + + alias ImmIntArr = immutable(int[]); + static assert(is(Unconst!ImmIntArr == immutable(int)[])); +} + +/** +Removes all qualifiers, if any, from type `T`. */ template Unqual(T) { - version (none) // Error: recursive alias declaration @@@BUG1308@@@ - { - static if (is(T U == const U)) alias Unqual = Unqual!U; - else static if (is(T U == immutable U)) alias Unqual = Unqual!U; - else static if (is(T U == inout U)) alias Unqual = Unqual!U; - else static if (is(T U == shared U)) alias Unqual = Unqual!U; - else alias Unqual = T; - } - else // workaround - { - static if (is(T U == immutable U)) alias Unqual = U; - else static if (is(T U == shared inout const U)) alias Unqual = U; - else static if (is(T U == shared inout U)) alias Unqual = U; - else static if (is(T U == shared const U)) alias Unqual = U; - else static if (is(T U == shared U)) alias Unqual = U; - else static if (is(T U == inout const U)) alias Unqual = U; - else static if (is(T U == inout U)) alias Unqual = U; - else static if (is(T U == const U)) alias Unqual = U; - else alias Unqual = T; - } + import core.internal.traits : CoreUnqual = Unqual; + alias Unqual = CoreUnqual!(T); } /// @@ -6944,14 +7866,14 @@ package template ModifyTypePreservingTQ(alias Modifier, T) } /** - * Copies type qualifiers from $(D FromType) to $(D ToType). + * Copies type qualifiers from `FromType` to `ToType`. * * Supported type qualifiers: * $(UL - * $(LI $(D const)) - * $(LI $(D inout)) - * $(LI $(D immutable)) - * $(LI $(D shared)) + * $(LI `const`) + * $(LI `inout`) + * $(LI `immutable`) + * $(LI `shared`) * ) */ template CopyTypeQualifiers(FromType, ToType) @@ -6980,9 +7902,9 @@ template CopyTypeQualifiers(FromType, ToType) } /** -Returns the type of `Target` with the "constness" of `Source`. A type's $(B constness) -refers to whether it is `const`, `immutable`, or `inout`. If `source` has no constness, the -returned type will be the same as `Target`. +Returns the type of `ToType` with the "constness" of `FromType`. A type's $(B constness) +refers to whether it is `const`, `immutable`, or `inout`. If `FromType` has no constness, the +returned type will be the same as `ToType`. */ template CopyConstness(FromType, ToType) { @@ -7054,9 +7976,9 @@ template CopyConstness(FromType, ToType) /** Returns the inferred type of the loop variable when a variable of type T -is iterated over using a $(D foreach) loop with a single loop variable and +is iterated over using a `foreach` loop with a single loop variable and automatically inferred return type. Note that this may not be the same as -$(D std.range.ElementType!Range) in the case of narrow strings, or if T +`std.range.ElementType!Range` in the case of narrow strings, or if T has both opApply and a range interface. */ template ForeachType(T) @@ -7083,23 +8005,30 @@ template ForeachType(T) /** - * Strips off all $(D enum)s from type $(D T). + * Strips off all `enum`s from type `T`. */ template OriginalType(T) { - template Impl(T) + static if (is(T == enum)) { - static if (is(T U == enum)) alias Impl = OriginalType!U; - else alias Impl = T; - } + template Impl(T) + { + static if (is(T U == enum)) alias Impl = OriginalType!U; + else alias Impl = T; + } - alias OriginalType = ModifyTypePreservingTQ!(Impl, T); + alias OriginalType = ModifyTypePreservingTQ!(Impl, T); + } + else + { + alias OriginalType = T; + } } /// @safe unittest { - enum E : real { a } + enum E : real { a = 0 } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle enum F : E { a = E.a } alias G = const(F); static assert(is(OriginalType!E == real)); @@ -7115,7 +8044,6 @@ alias KeyType(V : V[K], K) = K; /// @safe unittest { - import std.traits; alias Hash = int[string]; static assert(is(KeyType!Hash == string)); static assert(is(ValueType!Hash == int)); @@ -7131,7 +8059,6 @@ alias ValueType(V : V[K], K) = V; /// @safe unittest { - import std.traits; alias Hash = int[string]; static assert(is(KeyType!Hash == string)); static assert(is(ValueType!Hash == int)); @@ -7140,8 +8067,14 @@ alias ValueType(V : V[K], K) = V; } /** - * Returns the corresponding unsigned type for T. T must be a numeric - * integral type, otherwise a compile-time error occurs. +Params: + T = A built in integral or vector type. + +Returns: + The corresponding unsigned numeric type for `T` with the + same type qualifiers. + + If `T` is not a integral or vector, a compile-time error is given. */ template Unsigned(T) { @@ -7167,6 +8100,27 @@ template Unsigned(T) alias Unsigned = ModifyTypePreservingTQ!(Impl, OriginalType!T); } +/// +@safe unittest +{ + static assert(is(Unsigned!(int) == uint)); + static assert(is(Unsigned!(long) == ulong)); + static assert(is(Unsigned!(const short) == const ushort)); + static assert(is(Unsigned!(immutable byte) == immutable ubyte)); + static assert(is(Unsigned!(inout int) == inout uint)); +} + + +/// Unsigned types are forwarded +@safe unittest +{ + static assert(is(Unsigned!(uint) == uint)); + static assert(is(Unsigned!(const uint) == const uint)); + + static assert(is(Unsigned!(ubyte) == ubyte)); + static assert(is(Unsigned!(immutable uint) == immutable uint)); +} + @safe unittest { alias U1 = Unsigned!int; @@ -7201,7 +8155,8 @@ Returns the largest type, i.e. T such that T.sizeof is the largest. If more than one type is of the same size, the leftmost argument of these in will be returned. */ -template Largest(T...) if (T.length >= 1) +template Largest(T...) +if (T.length >= 1) { static if (T.length == 1) { @@ -7296,7 +8251,7 @@ template Signed(T) Returns the most negative value of the numeric type T. */ template mostNegative(T) - if (isNumeric!T || isSomeChar!T || isBoolean!T) +if (isNumeric!T || isSomeChar!T || isBoolean!T) { static if (is(typeof(T.min_normal))) enum mostNegative = -T.max; @@ -7318,10 +8273,12 @@ template mostNegative(T) /// @safe unittest { - foreach (T; AliasSeq!(bool, byte, short, int, long)) + import std.meta : AliasSeq; + + static foreach (T; AliasSeq!(bool, byte, short, int, long)) static assert(mostNegative!T == T.min); - foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar)) + static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar)) static assert(mostNegative!T == 0); } @@ -7330,7 +8287,7 @@ Get the type that a scalar type `T` will $(LINK2 $(ROOT_DIR)spec/type.html#integ to in multi-term arithmetic expressions. */ template Promoted(T) - if (isScalarType!T) +if (isScalarType!T) { alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init)); } @@ -7350,14 +8307,14 @@ template Promoted(T) @safe unittest { // promote to int: - foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar)) + static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar)) { static assert(is(Promoted!T == int)); static assert(is(Promoted!(shared(const T)) == shared(const int))); } // already promoted: - foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real)) + static foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real)) { static assert(is(Promoted!T == T)); static assert(is(Promoted!(immutable(T)) == immutable(T))); @@ -7369,14 +8326,14 @@ template Promoted(T) //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// /** -Returns the mangled name of symbol or type $(D sth). +Returns the mangled name of symbol or type `sth`. -$(D mangledName) is the same as builtin $(D .mangleof) property, but +`mangledName` is the same as builtin `.mangleof` property, but might be more convenient in generic code, e.g. as a template argument when invoking staticMap. */ template mangledName(sth...) - if (sth.length == 1) +if (sth.length == 1) { enum string mangledName = sth[0].mangleof; } @@ -7384,11 +8341,12 @@ template mangledName(sth...) /// @safe unittest { + import std.meta : AliasSeq; alias TL = staticMap!(mangledName, int, const int, immutable int); static assert(TL == AliasSeq!("i", "xi", "yi")); } -version (unittest) void freeFunc(string); +version (StdUnittest) private void freeFunc(string); @safe unittest { @@ -7400,7 +8358,8 @@ version (unittest) void freeFunc(string); static assert(mangledName!mangledName == "3std6traits11mangledName"); static assert(mangledName!freeFunc == "_D3std6traits8freeFuncFAyaZv"); int x; - static if (is(typeof({ return x; }) : int delegate() pure)) // issue 9148 + // https://issues.dlang.org/show_bug.cgi?id=9148 + static if (is(typeof({ return x; }) : int delegate() pure)) static assert(mangledName!((int a) { return a+x; }) == "DFNaNbNiNfiZi"); // pure nothrow @safe @nogc else static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc @@ -7409,7 +8368,7 @@ version (unittest) void freeFunc(string); @system unittest { // @system due to demangle - // Test for bug 5718 + // Test for https://issues.dlang.org/show_bug.cgi?id=5718 import std.demangle : demangle; int foo; auto foo_demangled = demangle(mangledName!foo); @@ -7426,10 +8385,11 @@ version (unittest) void freeFunc(string); // XXX Select & select should go to another module. (functional or algorithm?) /** -Aliases itself to $(D T[0]) if the boolean $(D condition) is $(D true) -and to $(D T[1]) otherwise. +Aliases itself to `T[0]` if the boolean `condition` is `true` +and to `T[1]` otherwise. */ -template Select(bool condition, T...) if (T.length == 2) +template Select(bool condition, T...) +if (T.length == 2) { import std.meta : Alias; alias Select = Alias!(T[!condition]); @@ -7458,19 +8418,28 @@ template Select(bool condition, T...) if (T.length == 2) } /** -If $(D cond) is $(D true), returns $(D a) without evaluating $(D -b). Otherwise, returns $(D b) without evaluating $(D a). +Select one of two functions to run via template parameter. + +Params: + cond = A `bool` which determines which function is run + a = The first function + b = The second function + +Returns: + `a` without evaluating `b` if `cond` is `true`. + Otherwise, returns `b` without evaluating `a`. */ A select(bool cond : true, A, B)(A a, lazy B b) { return a; } /// Ditto B select(bool cond : false, A, B)(lazy A a, B b) { return b; } +/// @safe unittest { - real pleasecallme() { return 0; } - int dontcallme() { assert(0); } - auto a = select!true(pleasecallme(), dontcallme()); - auto b = select!false(dontcallme(), pleasecallme()); + real run() { return 0; } + int fail() { assert(0); } + auto a = select!true(run(), fail()); + auto b = select!false(fail(), run()); static assert(is(typeof(a) == real)); static assert(is(typeof(b) == real)); } @@ -7591,28 +8560,7 @@ template getUDAs(alias symbol, alias attribute) { import std.meta : Filter; - template isDesiredUDA(alias toCheck) - { - static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) - { - static if (__traits(compiles, toCheck == attribute)) - enum isDesiredUDA = toCheck == attribute; - else - enum isDesiredUDA = false; - } - else static if (is(typeof(toCheck))) - { - static if (__traits(isTemplate, attribute)) - enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); - else - enum isDesiredUDA = is(typeof(toCheck) == attribute); - } - else static if (__traits(isTemplate, attribute)) - enum isDesiredUDA = isInstanceOf!(attribute, toCheck); - else - enum isDesiredUDA = is(toCheck == attribute); - } - alias getUDAs = Filter!(isDesiredUDA, __traits(getAttributes, symbol)); + alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, symbol)); } /// @@ -7717,37 +8665,46 @@ template getUDAs(alias symbol, alias attribute) static assert(getUDAs!(i, 'c').length == 0); } -/** - * Gets all symbols within `symbol` that have the given user-defined attribute. - * This is not recursive; it will not search for symbols within symbols such as - * nested structs or unions. - */ -template getSymbolsByUDA(alias symbol, alias attribute) +private template isDesiredUDA(alias attribute) { - import std.format : format; - import std.meta : AliasSeq, Filter; - - // translate a list of strings into symbols. mixing in the entire alias - // avoids trying to access the symbol, which could cause a privacy violation - template toSymbols(names...) + template isDesiredUDA(alias toCheck) { - static if (names.length == 0) - alias toSymbols = AliasSeq!(); + static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) + { + static if (__traits(compiles, toCheck == attribute)) + enum isDesiredUDA = toCheck == attribute; + else + enum isDesiredUDA = false; + } + else static if (is(typeof(toCheck))) + { + static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); + else + enum isDesiredUDA = is(typeof(toCheck) == attribute); + } + else static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, toCheck); else - mixin("alias toSymbols = AliasSeq!(symbol.%s, toSymbols!(names[1..$]));" - .format(names[0])); + enum isDesiredUDA = is(toCheck == attribute); } +} - // filtering inaccessible members - enum isAccessibleMember(string name) = __traits(compiles, __traits(getMember, symbol, name)); - alias accessibleMembers = Filter!(isAccessibleMember, __traits(allMembers, symbol)); +/** +Params: + symbol = The aggregate type or module to search + attribute = The user-defined attribute to search for - // filtering not compiled members such as alias of basic types - enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol." ~ name ~ ", attribute)"); - enum isCorrectMember(string name) = __traits(compiles, hasSpecificUDA!(name)); +Returns: + All symbols within `symbol` that have the given UDA `attribute`. - alias correctMembers = Filter!(isCorrectMember, accessibleMembers); - alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, correctMembers)); +Note: + This is not recursive; it will not search for symbols within symbols such as + nested structs or unions. + */ +template getSymbolsByUDA(alias symbol, alias attribute) +{ + alias membersWithUDA = getSymbolsByUDAImpl!(symbol, attribute, __traits(allMembers, symbol)); // if the symbol itself has the UDA, tack it on to the front of the list static if (hasUDA!(symbol, attribute)) @@ -7760,6 +8717,20 @@ template getSymbolsByUDA(alias symbol, alias attribute) @safe unittest { enum Attr; + struct A + { + @Attr int a; + int b; + } + + static assert(getSymbolsByUDA!(A, Attr).length == 1); + static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); +} + +/// +@safe unittest +{ + enum Attr; static struct A { @@ -7780,7 +8751,11 @@ template getSymbolsByUDA(alias symbol, alias attribute) // Can access attributes on the symbols returned by getSymbolsByUDA. static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr)); +} +/// Finds multiple attributes +@safe unittest +{ static struct UDA { string name; } static struct B @@ -7799,6 +8774,12 @@ template getSymbolsByUDA(alias symbol, alias attribute) static assert(getSymbolsByUDA!(B, 100).length == 1); // Can get the value of the UDA from the return value static assert(getUDAs!(getSymbolsByUDA!(B, UDA)[0], UDA)[0].name == "X"); +} + +/// Checks for UDAs on the aggregate symbol itself +@safe unittest +{ + static struct UDA { string name; } @UDA("A") static struct C @@ -7807,77 +8788,175 @@ template getSymbolsByUDA(alias symbol, alias attribute) int d; } - // Also checks the symbol itself static assert(getSymbolsByUDA!(C, UDA).length == 2); static assert(getSymbolsByUDA!(C, UDA)[0].stringof == "C"); static assert(getSymbolsByUDA!(C, UDA)[1].stringof == "d"); +} + +/// Finds nothing if there is no member with specific UDA +@safe unittest +{ + static struct UDA { string name; } static struct D { int x; } - //Finds nothing if there is no member with specific UDA - static assert(getSymbolsByUDA!(D,UDA).length == 0); + static assert(getSymbolsByUDA!(D, UDA).length == 0); +} + +// https://issues.dlang.org/show_bug.cgi?id=18314 +@safe unittest +{ + enum attr1; + enum attr2; + + struct A + { + @attr1 + int n; + // Removed due to https://issues.dlang.org/show_bug.cgi?id=16206 + //@attr1 + //void foo()(string){} + @attr1 + void foo(); + @attr2 + void foo(int a); + } + + static assert(getSymbolsByUDA!(A, attr1).length == 2); + static assert(getSymbolsByUDA!(A, attr2).length == 1); } -// #15335: getSymbolsByUDA fails if type has private members +// getSymbolsByUDA fails if type has private members +// https://issues.dlang.org/show_bug.cgi?id=15335 @safe unittest { // HasPrivateMembers has, well, private members, one of which has a UDA. import std.internal.test.uda : Attr, HasPrivateMembers; // Trying access to private member from another file therefore we do not have access // for this otherwise we get deprecation warning - not visible from module - static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1); + // This line is commented because `__traits(getMember)` should also consider + // private members; this is not currently the case, but the PR that + // fixes `__traits(getMember)` is blocked by this specific test. + //static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1); static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr)); } -/// +// getSymbolsByUDA works with structs but fails with classes +// https://issues.dlang.org/show_bug.cgi?id=16387 @safe unittest { enum Attr; - struct A + class A { - alias int INT; - alias void function(INT) SomeFunction; - @Attr int a; - int b; - @Attr private int c; - private int d; + @Attr uint a; } - // Here everything is fine, we have access to private member c - static assert(getSymbolsByUDA!(A, Attr).length == 2); - static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); - static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr)); + alias res = getSymbolsByUDA!(A, Attr); + static assert(res.length == 1); + static assert(res[0].stringof == "a"); +} + +// getSymbolsByUDA fails on AliasSeq members +// https://issues.dlang.org/show_bug.cgi?id=18884 +@safe unittest +{ + struct X + { + alias A = AliasSeq!(ulong, uint); + } + + static assert(is(getSymbolsByUDA!(X, X) == AliasSeq!())); } -// #16387: getSymbolsByUDA works with structs but fails with classes +// getSymbolsByUDA produces wrong result if one of the symbols having the UDA is a function +// https://issues.dlang.org/show_bug.cgi?id=18624 @safe unittest { enum Attr; - class A + struct A { - @Attr uint a; + @Attr void a(); + @Attr void a(int n); + void b(); + @Attr void c(); } - alias res = getSymbolsByUDA!(A, Attr); - static assert(res.length == 1); - static assert(res[0].stringof == "a"); + static assert(getSymbolsByUDA!(A, Attr).stringof == "tuple(a, a, c)"); +} + +// getSymbolsByUDA no longer works on modules +// https://issues.dlang.org/show_bug.cgi?id=20054 +version (StdUnittest) +{ + @("Issue20054") + void issue20054() {} + static assert(__traits(compiles, getSymbolsByUDA!(mixin(__MODULE__), "Issue20054"))); +} + +private template getSymbolsByUDAImpl(alias symbol, alias attribute, names...) +{ + import std.meta : Alias, AliasSeq, Filter; + static if (names.length == 0) + { + alias getSymbolsByUDAImpl = AliasSeq!(); + } + else + { + alias tail = getSymbolsByUDAImpl!(symbol, attribute, names[1 .. $]); + + // Filtering inaccessible members. + static if (!__traits(compiles, __traits(getMember, symbol, names[0]))) + { + alias getSymbolsByUDAImpl = tail; + } + else + { + alias member = __traits(getMember, symbol, names[0]); + + // Filtering not compiled members such as alias of basic types. + static if (!__traits(compiles, hasUDA!(member, attribute))) + { + alias getSymbolsByUDAImpl = tail; + } + // Get overloads for functions, in case different overloads have different sets of UDAs. + else static if (isFunction!member) + { + enum hasSpecificUDA(alias member) = hasUDA!(member, attribute); + alias overloadsWithUDA = Filter!(hasSpecificUDA, __traits(getOverloads, symbol, names[0])); + alias getSymbolsByUDAImpl = AliasSeq!(overloadsWithUDA, tail); + } + else static if (hasUDA!(member, attribute)) + { + alias getSymbolsByUDAImpl = AliasSeq!(member, tail); + } + else + { + alias getSymbolsByUDAImpl = tail; + } + } + } } /** - Returns: $(D true) iff all types $(D T) are the same. + Returns: `true` iff all types `T` are the same. */ template allSameType(T...) { - static if (T.length <= 1) + static foreach (idx, Ti; T) { - enum bool allSameType = true; + static if (idx + 1 < T.length && + !is(typeof(allSameType) == bool) && + !is(T[idx] == T[idx + 1])) + { + enum bool allSameType = false; + } } - else + static if (!is(typeof(allSameType) == bool)) { - enum bool allSameType = is(T[0] == T[1]) && allSameType!(T[1..$]); + enum bool allSameType = true; } } @@ -7894,7 +8973,7 @@ template allSameType(T...) } /** - Returns: $(D true) iff the type $(D T) can be tested in an $(D + Returns: `true` iff the type `T` can be tested in an $(D if)-expression, that is if $(D if (pred(T.init)) {}) is compilable. */ enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) {} }); @@ -7914,7 +8993,8 @@ enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) * Returns: * `true` if `X` is a type, `false` otherwise */ -template isType(X...) if (X.length == 1) +template isType(X...) +if (X.length == 1) { enum isType = is(X[0]); } @@ -7957,7 +9037,8 @@ template isType(X...) if (X.length == 1) * Use $(LREF isFunctionPointer) or $(LREF isDelegate) for detecting those types * respectively. */ -template isFunction(X...) if (X.length == 1) +template isFunction(X...) +if (X.length == 1) { static if (is(typeof(&X[0]) U : U*) && is(U == function) || is(typeof(&X[0]) U == delegate)) @@ -7993,7 +9074,8 @@ template isFunction(X...) if (X.length == 1) * Returns: * `true` if `X` is final, `false` otherwise */ -template isFinal(X...) if (X.length == 1) +template isFinal(X...) +if (X.length == 1) { static if (is(X[0] == class)) enum isFinal = __traits(isFinalClass, X[0]); @@ -8033,16 +9115,14 @@ template isFinal(X...) if (X.length == 1) + Returns: + `true` if `S` can be copied. `false` otherwise. + ++/ -enum isCopyable(S) = is(typeof( - { S foo = S.init; S copy = foo; } -)); +enum isCopyable(S) = __traits(isCopyable, S); /// @safe unittest { struct S1 {} // Fine. Can be copied struct S2 { this(this) {}} // Fine. Can be copied - struct S3 {@disable this(this) {}} // Not fine. Copying is disabled. + struct S3 {@disable this(this); } // Not fine. Copying is disabled. struct S4 {S3 s;} // Not fine. A field has copying disabled. class C1 {} |