| // Written in the D programming language. |
| |
| /** |
| * Templates which extract information about types and symbols at compile time. |
| * |
| * $(SCRIPT inhibitQuickIndex = 1;) |
| * |
| * $(DIVC quickindex, |
| * $(BOOKTABLE , |
| * $(TR $(TH Category) $(TH Templates)) |
| * $(TR $(TD Symbol Name _traits) $(TD |
| * $(LREF fullyQualifiedName) |
| * $(LREF moduleName) |
| * $(LREF packageName) |
| * )) |
| * $(TR $(TD Function _traits) $(TD |
| * $(LREF isFunction) |
| * $(LREF arity) |
| * $(LREF functionAttributes) |
| * $(LREF hasFunctionAttributes) |
| * $(LREF functionLinkage) |
| * $(LREF FunctionTypeOf) |
| * $(LREF isSafe) |
| * $(LREF isUnsafe) |
| * $(LREF isFinal) |
| * $(LREF ParameterDefaults) |
| * $(LREF ParameterIdentifierTuple) |
| * $(LREF ParameterStorageClassTuple) |
| * $(LREF Parameters) |
| * $(LREF ReturnType) |
| * $(LREF SetFunctionAttributes) |
| * $(LREF variadicFunctionStyle) |
| * )) |
| * $(TR $(TD Aggregate Type _traits) $(TD |
| * $(LREF BaseClassesTuple) |
| * $(LREF BaseTypeTuple) |
| * $(LREF classInstanceAlignment) |
| * $(LREF EnumMembers) |
| * $(LREF FieldNameTuple) |
| * $(LREF Fields) |
| * $(LREF hasAliasing) |
| * $(LREF hasElaborateAssign) |
| * $(LREF hasElaborateCopyConstructor) |
| * $(LREF hasElaborateDestructor) |
| * $(LREF hasIndirections) |
| * $(LREF hasMember) |
| * $(LREF hasStaticMember) |
| * $(LREF hasNested) |
| * $(LREF hasUnsharedAliasing) |
| * $(LREF InterfacesTuple) |
| * $(LREF isInnerClass) |
| * $(LREF isNested) |
| * $(LREF MemberFunctionsTuple) |
| * $(LREF RepresentationTypeTuple) |
| * $(LREF TemplateArgsOf) |
| * $(LREF TemplateOf) |
| * $(LREF TransitiveBaseTypeTuple) |
| * )) |
| * $(TR $(TD Type Conversion) $(TD |
| * $(LREF CommonType) |
| * $(LREF ImplicitConversionTargets) |
| * $(LREF CopyTypeQualifiers) |
| * $(LREF CopyConstness) |
| * $(LREF isAssignable) |
| * $(LREF isCovariantWith) |
| * $(LREF isImplicitlyConvertible) |
| * )) |
| * $(TR $(TD SomethingTypeOf) $(TD |
| * $(LREF rvalueOf) |
| * $(LREF lvalueOf) |
| * $(LREF InoutOf) |
| * $(LREF ConstOf) |
| * $(LREF SharedOf) |
| * $(LREF SharedInoutOf) |
| * $(LREF SharedConstOf) |
| * $(LREF ImmutableOf) |
| * $(LREF QualifierOf) |
| * )) |
| * $(TR $(TD Categories of types) $(TD |
| * $(LREF allSameType) |
| * $(LREF ifTestable) |
| * $(LREF isType) |
| * $(LREF isAggregateType) |
| * $(LREF isArray) |
| * $(LREF isAssociativeArray) |
| * $(LREF isAutodecodableString) |
| * $(LREF isBasicType) |
| * $(LREF isBoolean) |
| * $(LREF isBuiltinType) |
| * $(LREF isCopyable) |
| * $(LREF isDynamicArray) |
| * $(LREF isEqualityComparable) |
| * $(LREF isFloatingPoint) |
| * $(LREF isIntegral) |
| * $(LREF isNarrowString) |
| * $(LREF isConvertibleToString) |
| * $(LREF isNumeric) |
| * $(LREF isOrderingComparable) |
| * $(LREF isPointer) |
| * $(LREF isScalarType) |
| * $(LREF isSigned) |
| * $(LREF isSIMDVector) |
| * $(LREF isSomeChar) |
| * $(LREF isSomeString) |
| * $(LREF isStaticArray) |
| * $(LREF isUnsigned) |
| * )) |
| * $(TR $(TD Type behaviours) $(TD |
| * $(LREF isAbstractClass) |
| * $(LREF isAbstractFunction) |
| * $(LREF isCallable) |
| * $(LREF isDelegate) |
| * $(LREF isExpressions) |
| * $(LREF isFinalClass) |
| * $(LREF isFinalFunction) |
| * $(LREF isFunctionPointer) |
| * $(LREF isInstanceOf) |
| * $(LREF isIterable) |
| * $(LREF isMutable) |
| * $(LREF isSomeFunction) |
| * $(LREF isTypeTuple) |
| * )) |
| * $(TR $(TD General Types) $(TD |
| * $(LREF ForeachType) |
| * $(LREF KeyType) |
| * $(LREF Largest) |
| * $(LREF mostNegative) |
| * $(LREF OriginalType) |
| * $(LREF PointerTarget) |
| * $(LREF Signed) |
| * $(LREF Unqual) |
| * $(LREF Unsigned) |
| * $(LREF ValueType) |
| * $(LREF Promoted) |
| * )) |
| * $(TR $(TD Misc) $(TD |
| * $(LREF mangledName) |
| * $(LREF Select) |
| * $(LREF select) |
| * )) |
| * $(TR $(TD User-Defined Attributes) $(TD |
| * $(LREF hasUDA) |
| * $(LREF getUDAs) |
| * $(LREF getSymbolsByUDA) |
| * )) |
| * ) |
| * ) |
| * |
| * Copyright: Copyright Digital Mars 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)), |
| * $(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) |
| */ |
| /* Copyright Digital Mars 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; |
| |
| // Legacy inheritance from std.typetuple |
| // See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797 |
| import std.meta : staticMapMeta = staticMap; |
| // TODO: find a way to trigger deprecation warnings |
| //deprecated("staticMap is part of std.meta: Please import std.meta") |
| alias staticMap = staticMapMeta; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Functions |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // 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); |
| alias SignedCentTypeList = AliasSeq!(cent); |
| alias UnsignedCentTypeList = AliasSeq!(ucent); |
| } |
| else |
| { |
| alias CentTypeList = AliasSeq!(); |
| alias SignedCentTypeList = AliasSeq!(); |
| alias UnsignedCentTypeList = AliasSeq!(); |
| } |
| |
| alias IntegralTypeList = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList); |
| alias SignedIntTypeList = AliasSeq!(byte, short, int, long, SignedCentTypeList); |
| alias UnsignedIntTypeList = AliasSeq!(ubyte, ushort, uint, ulong, UnsignedCentTypeList); |
| alias FloatingPointTypeList = AliasSeq!(float, double, real); |
| alias ImaginaryTypeList = AliasSeq!(ifloat, idouble, ireal); |
| alias ComplexTypeList = AliasSeq!(cfloat, cdouble, creal); |
| alias NumericTypeList = AliasSeq!(IntegralTypeList, FloatingPointTypeList); |
| alias CharTypeList = AliasSeq!(char, wchar, dchar); |
| } |
| |
| package |
| { |
| // Add the mutable qualifier to the given type T. |
| template MutableOf(T) { alias MutableOf = T ; } |
| } |
| |
| /// 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) ; } |
| |
| @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)); |
| static assert(is(SharedInoutOf!int == shared inout int)); |
| static assert(is(SharedConstOf!int == shared const int)); |
| static assert(is( ImmutableOf!int == immutable int)); |
| } |
| |
| /// Get 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; |
| } |
| |
| @safe unittest |
| { |
| alias Qual1 = QualifierOf!( int); static assert(is(Qual1!long == long)); |
| alias Qual2 = QualifierOf!( inout int); static assert(is(Qual2!long == inout long)); |
| alias Qual3 = QualifierOf!( const int); static assert(is(Qual3!long == const long)); |
| alias Qual4 = QualifierOf!(shared int); static assert(is(Qual4!long == shared long)); |
| alias Qual5 = QualifierOf!(shared inout int); static assert(is(Qual5!long == shared inout long)); |
| alias Qual6 = QualifierOf!(shared const int); static assert(is(Qual6!long == shared const long)); |
| alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long)); |
| } |
| |
| version (unittest) |
| { |
| alias TypeQualifierList = AliasSeq!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf); |
| |
| struct SubTypeOf(T) |
| { |
| T val; |
| alias val this; |
| } |
| } |
| |
| private alias parentOf(alias sym) = Identity!(__traits(parent, sym)); |
| private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__traits(parent, T)); |
| |
| /** |
| * Get the full package name for the given symbol. |
| */ |
| template packageName(alias T) |
| { |
| import std.algorithm.searching : startsWith; |
| |
| static if (__traits(compiles, parentOf!T)) |
| enum parent = packageName!(parentOf!T); |
| else |
| enum string parent = null; |
| |
| static if (T.stringof.startsWith("package ")) |
| enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $]; |
| else static if (parent) |
| enum packageName = parent; |
| else |
| static assert(false, T.stringof ~ " has no parent"); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.traits; |
| static assert(packageName!packageName == "std"); |
| } |
| |
| @safe unittest |
| { |
| import std.array; |
| |
| // Commented out because of dmd @@@BUG8922@@@ |
| // static assert(packageName!std == "std"); // this package (currently: "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 |
| |
| import core.sync.barrier; // local import |
| static assert(packageName!core == "core"); |
| static assert(packageName!(core.sync) == "core.sync"); |
| static assert(packageName!Barrier == "core.sync"); |
| |
| struct X12287(T) { T i; } |
| static assert(packageName!(X12287!int.i) == "std"); |
| } |
| |
| version (none) version (unittest) //Please uncomment me when changing packageName to test global imports |
| { |
| import core.sync.barrier; // global import |
| static assert(packageName!core == "core"); |
| static assert(packageName!(core.sync) == "core.sync"); |
| static assert(packageName!Barrier == "core.sync"); |
| } |
| |
| /** |
| * Get the module name (including package) for the given symbol. |
| */ |
| template moduleName(alias T) |
| { |
| import std.algorithm.searching : startsWith; |
| |
| static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package"); |
| |
| static if (T.stringof.startsWith("module ")) |
| { |
| static if (__traits(compiles, packageName!T)) |
| enum packagePrefix = packageName!T ~ '.'; |
| else |
| enum packagePrefix = ""; |
| |
| enum moduleName = packagePrefix ~ T.stringof[7..$]; |
| } |
| else |
| alias moduleName = moduleName!(parentOf!T); // If you use enum, it will cause compiler ICE |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.traits; |
| static assert(moduleName!moduleName == "std.traits"); |
| } |
| |
| @safe unittest |
| { |
| import std.array; |
| |
| static assert(!__traits(compiles, moduleName!std)); |
| static assert(moduleName!(std.traits) == "std.traits"); // this module |
| static assert(moduleName!moduleName == "std.traits"); // symbol in this module |
| static assert(moduleName!(std.array) == "std.array"); // other module |
| static assert(moduleName!(std.array.array) == "std.array"); // symbol in other module |
| |
| import core.sync.barrier; // local import |
| static assert(!__traits(compiles, moduleName!(core.sync))); |
| static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); |
| static assert(moduleName!Barrier == "core.sync.barrier"); |
| |
| struct X12287(T) { T i; } |
| static assert(moduleName!(X12287!int.i) == "std.traits"); |
| } |
| |
| version (none) version (unittest) //Please uncomment me when changing moduleName to test global imports |
| { |
| import core.sync.barrier; // global import |
| static assert(!__traits(compiles, moduleName!(core.sync))); |
| static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); |
| static assert(moduleName!Barrier == "core.sync.barrier"); |
| } |
| |
| /*** |
| * Get the fully qualified name of a type or a symbol. Can act as an intelligent type/symbol to string converter. |
| |
| Example: |
| ----------------- |
| module myModule; |
| struct MyStruct {} |
| static assert(fullyQualifiedName!(const MyStruct[]) == "const(myModule.MyStruct[])"); |
| ----------------- |
| */ |
| template fullyQualifiedName(T...) |
| if (T.length == 1) |
| { |
| |
| static if (is(T)) |
| enum fullyQualifiedName = fqnType!(T[0], false, false, false, false); |
| else |
| enum fullyQualifiedName = fqnSym!(T[0]); |
| } |
| |
| /// |
| @safe unittest |
| { |
| static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName"); |
| } |
| |
| version (unittest) |
| { |
| // Used for both fqnType and fqnSym unittests |
| private struct QualifiedNameTests |
| { |
| struct Inner |
| { |
| } |
| |
| ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 ); |
| ref const(Inner[string]) retfunc( return ref Inner var1 ); |
| Inner inoutFunc(inout Inner) inout; |
| shared(const(Inner[string])[]) data; |
| const Inner delegate(double, string) @safe nothrow deleg; |
| inout(int) delegate(inout int) inout inoutDeleg; |
| Inner function(out double, string) funcPtr; |
| extern(C) Inner function(double, string) cFuncPtr; |
| |
| extern(C) void cVarArg(int, ...); |
| void dVarArg(...); |
| void dVarArg2(int, ...); |
| void typesafeVarArg(int[] ...); |
| |
| Inner[] array; |
| Inner[16] sarray; |
| Inner[Inner] aarray; |
| const(Inner[const(Inner)]) qualAarray; |
| |
| shared(immutable(Inner) delegate(ref double, scope string) const shared @trusted nothrow) attrDeleg; |
| |
| struct Data(T) { int x; } |
| void tfunc(T...)(T args) {} |
| |
| template Inst(alias A) { int x; } |
| |
| class Test12309(T, int x, string s) {} |
| } |
| |
| private enum QualifiedEnum |
| { |
| a = 42 |
| } |
| } |
| |
| private template fqnSym(alias T : X!A, alias X, A...) |
| { |
| template fqnTuple(T...) |
| { |
| static if (T.length == 0) |
| enum fqnTuple = ""; |
| else static if (T.length == 1) |
| { |
| static if (isExpressionTuple!T) |
| enum fqnTuple = T[0].stringof; |
| else |
| enum fqnTuple = fullyQualifiedName!(T[0]); |
| } |
| else |
| enum fqnTuple = fqnTuple!(T[0]) ~ ", " ~ fqnTuple!(T[1 .. $]); |
| } |
| |
| enum fqnSym = |
| fqnSym!(__traits(parent, X)) ~ |
| '.' ~ __traits(identifier, X) ~ "!(" ~ fqnTuple!A ~ ")"; |
| } |
| |
| private template fqnSym(alias T) |
| { |
| static if (__traits(compiles, __traits(parent, T)) && !__traits(isSame, T, __traits(parent, T))) |
| enum parentPrefix = fqnSym!(__traits(parent, T)) ~ "."; |
| else |
| enum parentPrefix = null; |
| |
| static string adjustIdent(string s) |
| { |
| import std.algorithm.searching : findSplit, skipOver; |
| |
| if (s.skipOver("package ") || s.skipOver("module ")) |
| return s; |
| return s.findSplit("(")[0]; |
| } |
| enum fqnSym = parentPrefix ~ adjustIdent(__traits(identifier, T)); |
| } |
| |
| @safe unittest |
| { |
| alias fqn = fullyQualifiedName; |
| |
| // Make sure those 2 are the same |
| static assert(fqnSym!fqn == fqn!fqn); |
| |
| static assert(fqn!fqn == "std.traits.fullyQualifiedName"); |
| |
| alias qnTests = QualifiedNameTests; |
| enum prefix = "std.traits.QualifiedNameTests."; |
| static assert(fqn!(qnTests.Inner) == prefix ~ "Inner"); |
| static assert(fqn!(qnTests.func) == prefix ~ "func"); |
| static assert(fqn!(qnTests.Data!int) == prefix ~ "Data!(int)"); |
| static assert(fqn!(qnTests.Data!int.x) == prefix ~ "Data!(int).x"); |
| static assert(fqn!(qnTests.tfunc!(int[])) == prefix ~ "tfunc!(int[])"); |
| static assert(fqn!(qnTests.Inst!(Object)) == prefix ~ "Inst!(object.Object)"); |
| static assert(fqn!(qnTests.Inst!(Object).x) == prefix ~ "Inst!(object.Object).x"); |
| |
| static assert(fqn!(qnTests.Test12309!(int, 10, "str")) |
| == prefix ~ "Test12309!(int, 10, \"str\")"); |
| |
| import core.sync.barrier; |
| static assert(fqn!Barrier == "core.sync.barrier.Barrier"); |
| } |
| |
| @safe unittest |
| { |
| struct TemplatedStruct() |
| { |
| enum foo = 0; |
| } |
| alias TemplatedStructAlias = TemplatedStruct; |
| assert("TemplatedStruct.foo" == fullyQualifiedName!(TemplatedStructAlias!().foo)); |
| } |
| |
| private template fqnType(T, |
| bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout) |
| { |
| import std.format : format; |
| |
| // Convenience tags |
| enum { |
| _const = 0, |
| _immutable = 1, |
| _shared = 2, |
| _inout = 3 |
| } |
| |
| alias qualifiers = AliasSeq!(is(T == const), is(T == immutable), is(T == shared), is(T == inout)); |
| alias noQualifiers = AliasSeq!(false, false, false, false); |
| |
| string storageClassesString(uint psc)() @property |
| { |
| alias PSC = ParameterStorageClass; |
| |
| return format("%s%s%s%s%s", |
| psc & PSC.scope_ ? "scope " : "", |
| psc & PSC.return_ ? "return " : "", |
| psc & PSC.out_ ? "out " : "", |
| psc & PSC.ref_ ? "ref " : "", |
| psc & PSC.lazy_ ? "lazy " : "" |
| ); |
| } |
| |
| string parametersTypeString(T)() @property |
| { |
| alias parameters = Parameters!(T); |
| alias parameterStC = ParameterStorageClassTuple!(T); |
| |
| enum variadic = variadicFunctionStyle!T; |
| static if (variadic == Variadic.no) |
| enum variadicStr = ""; |
| else static if (variadic == Variadic.c) |
| enum variadicStr = ", ..."; |
| else static if (variadic == Variadic.d) |
| enum variadicStr = parameters.length ? ", ..." : "..."; |
| else static if (variadic == Variadic.typesafe) |
| enum variadicStr = " ..."; |
| else |
| static assert(0, "New variadic style has been added, please update fullyQualifiedName implementation"); |
| |
| static if (parameters.length) |
| { |
| import std.algorithm.iteration : map; |
| import std.array : join; |
| import std.meta : staticMap; |
| import std.range : zip; |
| |
| string result = join( |
| map!(a => format("%s%s", a[0], a[1]))( |
| zip([staticMap!(storageClassesString, parameterStC)], |
| [staticMap!(fullyQualifiedName, parameters)]) |
| ), |
| ", " |
| ); |
| |
| return result ~= variadicStr; |
| } |
| else |
| return variadicStr; |
| } |
| |
| string linkageString(T)() @property |
| { |
| enum linkage = functionLinkage!T; |
| |
| if (linkage != "D") |
| return format("extern(%s) ", linkage); |
| else |
| return ""; |
| } |
| |
| string functionAttributeString(T)() @property |
| { |
| alias FA = FunctionAttribute; |
| enum attrs = functionAttributes!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" : "" |
| ); |
| } |
| |
| string addQualifiers(string typeString, |
| bool addConst, bool addImmutable, bool addShared, bool addInout) |
| { |
| auto result = typeString; |
| if (addShared) |
| { |
| result = format("shared(%s)", result); |
| } |
| if (addConst || addImmutable || addInout) |
| { |
| result = format("%s(%s)", |
| addConst ? "const" : |
| addImmutable ? "immutable" : "inout", |
| result |
| ); |
| } |
| return result; |
| } |
| |
| // Convenience template to avoid copy-paste |
| template chain(string current) |
| { |
| enum chain = addQualifiers(current, |
| qualifiers[_const] && !alreadyConst, |
| qualifiers[_immutable] && !alreadyImmutable, |
| qualifiers[_shared] && !alreadyShared, |
| qualifiers[_inout] && !alreadyInout); |
| } |
| |
| static if (is(T == string)) |
| { |
| enum fqnType = "string"; |
| } |
| else static if (is(T == wstring)) |
| { |
| enum fqnType = "wstring"; |
| } |
| else static if (is(T == dstring)) |
| { |
| enum fqnType = "dstring"; |
| } |
| else static if (isBasicType!T && !is(T == enum)) |
| { |
| enum fqnType = chain!((Unqual!T).stringof); |
| } |
| else static if (isAggregateType!T || is(T == enum)) |
| { |
| enum fqnType = chain!(fqnSym!T); |
| } |
| else static if (isStaticArray!T) |
| { |
| enum fqnType = chain!( |
| format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length) |
| ); |
| } |
| else static if (isArray!T) |
| { |
| enum fqnType = chain!( |
| format("%s[]", 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)) |
| ); |
| } |
| 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 fqnType = chain!( |
| format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers), |
| 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) |
| ); |
| } |
| } |
| else static if (isPointer!T) |
| { |
| enum fqnType = chain!( |
| format("%s*", fqnType!(PointerTarget!T, qualifiers)) |
| ); |
| } |
| else static if (is(T : __vector(V[N]), V, size_t N)) |
| { |
| enum fqnType = chain!( |
| format("__vector(%s[%s])", fqnType!(V, qualifiers), N) |
| ); |
| } |
| else |
| // In case something is forgotten |
| static assert(0, "Unrecognized type " ~ T.stringof ~ ", can't convert to fully qualified string"); |
| } |
| |
| @safe unittest |
| { |
| import std.format : format; |
| alias fqn = fullyQualifiedName; |
| |
| // Verify those 2 are the same for simple case |
| alias Ambiguous = const(QualifiedNameTests.Inner); |
| static assert(fqn!Ambiguous == fqnType!(Ambiguous, false, false, false, false)); |
| |
| // Main tests |
| enum inner_name = "std.traits.QualifiedNameTests.Inner"; |
| with (QualifiedNameTests) |
| { |
| // Special cases |
| static assert(fqn!(string) == "string"); |
| static assert(fqn!(wstring) == "wstring"); |
| static assert(fqn!(dstring) == "dstring"); |
| static assert(fqn!(void) == "void"); |
| static assert(fqn!(const(void)) == "const(void)"); |
| static assert(fqn!(shared(void)) == "shared(void)"); |
| static assert(fqn!(shared const(void)) == "const(shared(void))"); |
| static assert(fqn!(shared inout(void)) == "inout(shared(void))"); |
| static assert(fqn!(shared inout const(void)) == "const(shared(void))"); |
| static assert(fqn!(inout(void)) == "inout(void)"); |
| static assert(fqn!(inout const(void)) == "const(void)"); |
| static assert(fqn!(immutable(void)) == "immutable(void)"); |
| |
| // Basic qualified name |
| static assert(fqn!(Inner) == inner_name); |
| static assert(fqn!(QualifiedEnum) == "std.traits.QualifiedEnum"); // type |
| static assert(fqn!(QualifiedEnum.a) == "std.traits.QualifiedEnum.a"); // symbol |
| |
| // Array types |
| static assert(fqn!(typeof(array)) == format("%s[]", inner_name)); |
| static assert(fqn!(typeof(sarray)) == format("%s[16]", inner_name)); |
| static assert(fqn!(typeof(aarray)) == format("%s[%s]", inner_name, inner_name)); |
| |
| // qualified key for AA |
| static assert(fqn!(typeof(qualAarray)) == format("const(%s[const(%s)])", inner_name, inner_name)); |
| |
| // Qualified composed data types |
| static assert(fqn!(typeof(data)) == format("shared(const(%s[string])[])", inner_name)); |
| |
| // Function types + function attributes |
| static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref", |
| inner_name, inner_name)); |
| static assert(fqn!(typeof(retfunc)) == format("const(%s[string])(return %s) ref", inner_name, inner_name)); |
| static assert(fqn!(typeof(inoutFunc)) == format("inout(%s(inout(%s)))", inner_name, inner_name)); |
| static assert(fqn!(typeof(deleg)) == format("const(%s delegate(double, string) nothrow @safe)", inner_name)); |
| static assert(fqn!(typeof(inoutDeleg)) == "inout(int) delegate(inout(int)) inout"); |
| static assert(fqn!(typeof(funcPtr)) == format("%s function(out double, string)", inner_name)); |
| static assert(fqn!(typeof(cFuncPtr)) == format("extern(C) %s function(double, string)", inner_name)); |
| |
| // Delegate type with qualified function type |
| static assert(fqn!(typeof(attrDeleg)) == format("shared(immutable(%s) "~ |
| "delegate(ref double, scope string) nothrow @trusted shared const)", inner_name)); |
| |
| // Variable argument function types |
| static assert(fqn!(typeof(cVarArg)) == "extern(C) void(int, ...)"); |
| static assert(fqn!(typeof(dVarArg)) == "void(...)"); |
| static assert(fqn!(typeof(dVarArg2)) == "void(int, ...)"); |
| static assert(fqn!(typeof(typesafeVarArg)) == "void(int[] ...)"); |
| |
| // SIMD vector |
| static if (is(__vector(float[4]))) |
| { |
| static assert(fqn!(__vector(float[4])) == "__vector(float[4])"); |
| } |
| } |
| } |
| |
| /*** |
| * 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) |
| * is not part of a type, but the attribute of the function |
| * (see template $(LREF functionAttributes)). |
| */ |
| template ReturnType(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| static if (is(FunctionTypeOf!func R == return)) |
| alias ReturnType = R; |
| else |
| static assert(0, "argument has no return type"); |
| } |
| |
| /// |
| @safe unittest |
| { |
| int foo(); |
| ReturnType!foo x; // x is declared as int |
| } |
| |
| @safe unittest |
| { |
| struct G |
| { |
| int opCall (int i) { return 1;} |
| } |
| |
| alias ShouldBeInt = ReturnType!G; |
| static assert(is(ShouldBeInt == int)); |
| |
| G g; |
| static assert(is(ReturnType!g == int)); |
| |
| G* p; |
| alias pg = ReturnType!p; |
| static assert(is(pg == int)); |
| |
| class C |
| { |
| int opCall (int i) { return 1;} |
| } |
| |
| static assert(is(ReturnType!C == int)); |
| |
| C c; |
| static assert(is(ReturnType!c == int)); |
| |
| class Test |
| { |
| int prop() @property { return 0; } |
| } |
| alias R_Test_prop = ReturnType!(Test.prop); |
| static assert(is(R_Test_prop == int)); |
| |
| alias R_dglit = ReturnType!((int a) { return a; }); |
| static assert(is(R_dglit == int)); |
| } |
| |
| /*** |
| 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). |
| */ |
| template Parameters(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| static if (is(FunctionTypeOf!func P == function)) |
| alias Parameters = P; |
| else |
| static assert(0, "argument has no parameters"); |
| } |
| |
| /// |
| @safe unittest |
| { |
| int foo(int, long); |
| void bar(Parameters!foo); // declares void bar(int, long); |
| void abc(Parameters!foo[1]); // declares void abc(long); |
| } |
| |
| /** |
| * Alternate name for $(LREF Parameters), kept for legacy compatibility. |
| */ |
| alias ParameterTypeTuple = Parameters; |
| |
| @safe unittest |
| { |
| int foo(int i, bool b) { return 0; } |
| static assert(is(ParameterTypeTuple!foo == AliasSeq!(int, bool))); |
| static assert(is(ParameterTypeTuple!(typeof(&foo)) == AliasSeq!(int, bool))); |
| |
| struct S { real opCall(real r, int i) { return 0.0; } } |
| S s; |
| static assert(is(ParameterTypeTuple!S == AliasSeq!(real, int))); |
| static assert(is(ParameterTypeTuple!(S*) == AliasSeq!(real, int))); |
| static assert(is(ParameterTypeTuple!s == AliasSeq!(real, int))); |
| |
| class Test |
| { |
| int prop() @property { return 0; } |
| } |
| alias P_Test_prop = ParameterTypeTuple!(Test.prop); |
| static assert(P_Test_prop.length == 0); |
| |
| alias P_dglit = ParameterTypeTuple!((int a){}); |
| static assert(P_dglit.length == 1); |
| static assert(is(P_dglit[0] == int)); |
| } |
| |
| /** |
| Returns the number of arguments of function $(D func). |
| arity is undefined for variadic functions. |
| */ |
| template arity(alias func) |
| if ( isCallable!func && variadicFunctionStyle!func == Variadic.no ) |
| { |
| enum size_t arity = Parameters!func.length; |
| } |
| |
| /// |
| @safe unittest |
| { |
| void foo(){} |
| static assert(arity!foo == 0); |
| void bar(uint){} |
| static assert(arity!bar == 1); |
| void variadicFoo(uint...){} |
| static assert(!__traits(compiles, arity!variadicFoo)); |
| } |
| |
| /** |
| Get tuple, one per function parameter, of the storage classes of the parameters. |
| Params: |
| func = function symbol or type of function, delegate, or pointer to function |
| Returns: |
| A tuple of ParameterStorageClass bits |
| */ |
| 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 |
| } |
| |
| /// ditto |
| template ParameterStorageClassTuple(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| alias Func = FunctionTypeOf!func; |
| |
| static if (is(Func PT == __parameters)) |
| { |
| template StorageClass(size_t i) |
| { |
| static if (i < PT.length) |
| { |
| alias StorageClass = AliasSeq!( |
| extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)), |
| StorageClass!(i + 1)); |
| } |
| else |
| alias StorageClass = AliasSeq!(); |
| } |
| alias ParameterStorageClassTuple = StorageClass!0; |
| } |
| else |
| { |
| static assert(0, func[0].stringof ~ " is not a function"); |
| alias ParameterStorageClassTuple = AliasSeq!(); |
| } |
| } |
| |
| /// |
| @safe unittest |
| { |
| alias STC = ParameterStorageClass; // shorten the enum name |
| |
| void func(ref int ctx, out real result, real param) |
| { |
| } |
| alias pstc = ParameterStorageClassTuple!func; |
| static assert(pstc.length == 3); // three parameters |
| static assert(pstc[0] == STC.ref_); |
| static assert(pstc[1] == STC.out_); |
| static assert(pstc[2] == STC.none); |
| } |
| |
| /***************** |
| * Convert string tuple Attribs to ParameterStorageClass bits |
| * Params: |
| * Attribs = string tuple |
| * Returns: |
| * ParameterStorageClass bits |
| */ |
| template extractParameterStorageClassFlags(Attribs...) |
| { |
| enum ParameterStorageClass extractParameterStorageClassFlags = () |
| { |
| auto result = ParameterStorageClass.none; |
| static if (Attribs.length > 0) |
| { |
| foreach (attrib; [Attribs]) |
| { |
| final switch (attrib) with (ParameterStorageClass) |
| { |
| case "scope": result |= scope_; break; |
| case "out": result |= out_; break; |
| case "ref": result |= ref_; break; |
| case "lazy": result |= lazy_; break; |
| case "return": result |= return_; break; |
| } |
| } |
| /* Mimic behavor of original version of ParameterStorageClassTuple() |
| * to avoid breaking existing code. |
| */ |
| if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_)) |
| result = ParameterStorageClass.return_; |
| } |
| return result; |
| }(); |
| } |
| |
| @safe unittest |
| { |
| alias STC = ParameterStorageClass; |
| |
| void noparam() {} |
| static assert(ParameterStorageClassTuple!noparam.length == 0); |
| |
| ref int test(scope int*, ref int, out int, lazy int, int, return ref int i) { return i; } |
| alias test_pstc = ParameterStorageClassTuple!test; |
| static assert(test_pstc.length == 6); |
| static assert(test_pstc[0] == STC.scope_); |
| static assert(test_pstc[1] == STC.ref_); |
| static assert(test_pstc[2] == STC.out_); |
| static assert(test_pstc[3] == STC.lazy_); |
| static assert(test_pstc[4] == STC.none); |
| static assert(test_pstc[5] == STC.return_); |
| |
| interface Test |
| { |
| void test_const(int) const; |
| void test_sharedconst(int) shared const; |
| } |
| Test testi; |
| |
| alias test_const_pstc = ParameterStorageClassTuple!(Test.test_const); |
| static assert(test_const_pstc.length == 1); |
| static assert(test_const_pstc[0] == STC.none); |
| |
| alias test_sharedconst_pstc = ParameterStorageClassTuple!(testi.test_sharedconst); |
| static assert(test_sharedconst_pstc.length == 1); |
| static assert(test_sharedconst_pstc[0] == STC.none); |
| |
| alias dglit_pstc = ParameterStorageClassTuple!((ref int a) {}); |
| static assert(dglit_pstc.length == 1); |
| static assert(dglit_pstc[0] == STC.ref_); |
| |
| // Bugzilla 9317 |
| static inout(int) func(inout int param) { return param; } |
| static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none); |
| } |
| |
| @safe unittest |
| { |
| // Bugzilla 14253 |
| static struct Foo { |
| ref Foo opAssign(ref Foo rhs) return { return this; } |
| } |
| |
| alias tup = ParameterStorageClassTuple!(__traits(getOverloads, Foo, "opAssign")[0]); |
| } |
| |
| |
| /** |
| Get, as a tuple, the identifiers of the parameters to a function symbol. |
| */ |
| template ParameterIdentifierTuple(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| static if (is(FunctionTypeOf!func PT == __parameters)) |
| { |
| template Get(size_t i) |
| { |
| static if (!isFunctionPointer!func && !isDelegate!func |
| // Unnamed parameters yield CT error. |
| && is(typeof(__traits(identifier, PT[i .. i+1])))) |
| { |
| enum Get = __traits(identifier, PT[i .. i+1]); |
| } |
| else |
| { |
| enum Get = ""; |
| } |
| } |
| } |
| else |
| { |
| static assert(0, func[0].stringof ~ "is not a function"); |
| |
| // Define dummy entities to avoid pointless errors |
| template Get(size_t i) { enum Get = ""; } |
| alias PT = AliasSeq!(); |
| } |
| |
| template Impl(size_t i = 0) |
| { |
| static if (i == PT.length) |
| alias Impl = AliasSeq!(); |
| else |
| alias Impl = AliasSeq!(Get!i, Impl!(i+1)); |
| } |
| |
| alias ParameterIdentifierTuple = Impl!(); |
| } |
| |
| /// |
| @safe unittest |
| { |
| int foo(int num, string name, int); |
| static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); |
| } |
| |
| @safe unittest |
| { |
| alias PIT = ParameterIdentifierTuple; |
| |
| void bar(int num, string name, int[] array){} |
| static assert([PIT!bar] == ["num", "name", "array"]); |
| |
| // might be changed in the future? |
| void function(int num, string name) fp; |
| static assert([PIT!fp] == ["", ""]); |
| |
| // might be changed in the future? |
| void delegate(int num, string name, int[long] aa) dg; |
| static assert([PIT!dg] == ["", "", ""]); |
| |
| interface Test |
| { |
| @property string getter(); |
| @property void setter(int a); |
| Test method(int a, long b, string c); |
| } |
| static assert([PIT!(Test.getter)] == []); |
| static assert([PIT!(Test.setter)] == ["a"]); |
| static assert([PIT!(Test.method)] == ["a", "b", "c"]); |
| |
| /+ |
| // depends on internal |
| void baw(int, string, int[]){} |
| static assert([PIT!baw] == ["_param_0", "_param_1", "_param_2"]); |
| |
| // depends on internal |
| void baz(AliasSeq!(int, string, int[]) args){} |
| static assert([PIT!baz] == ["_param_0", "_param_1", "_param_2"]); |
| +/ |
| } |
| |
| |
| /** |
| 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. |
| */ |
| template ParameterDefaults(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| alias param_names = ParameterIdentifierTuple!func; |
| static if (is(FunctionTypeOf!(func[0]) PT == __parameters)) |
| { |
| template Get(size_t i) |
| { |
| // `PT[i .. i+1]` declares a parameter with an arbitrary name. |
| // To avoid a name clash, generate local names that are distinct |
| // from the parameter name, and mix them in. |
| enum name = param_names[i]; |
| enum args = "args" ~ (name == "args" ? "_" : ""); |
| enum val = "val" ~ (name == "val" ? "_" : ""); |
| enum ptr = "ptr" ~ (name == "ptr" ? "_" : ""); |
| mixin(" |
| // workaround scope escape check, see |
| // https://issues.dlang.org/show_bug.cgi?id=16582 |
| // should use return scope once available |
| enum get = (PT[i .. i+1] " ~ args ~ ") @trusted |
| { |
| // If the parameter is lazy, we force it to be evaluated |
| // like this. |
| auto " ~ val ~ " = " ~ args ~ "[0]; |
| auto " ~ ptr ~ " = &" ~ val ~ "; |
| // workaround Bugzilla 16582 |
| return *" ~ ptr ~ "; |
| }; |
| "); |
| static if (is(typeof(get()))) |
| enum Get = get(); |
| else |
| alias Get = void; |
| // If default arg doesn't exist, returns void instead. |
| } |
| } |
| else |
| { |
| static assert(0, func[0].stringof ~ "is not a function"); |
| |
| // Define dummy entities to avoid pointless errors |
| template Get(size_t i) { enum Get = ""; } |
| alias PT = AliasSeq!(); |
| } |
| |
| template Impl(size_t i = 0) |
| { |
| static if (i == PT.length) |
| alias Impl = AliasSeq!(); |
| else |
| alias Impl = AliasSeq!(Get!i, Impl!(i+1)); |
| } |
| |
| alias ParameterDefaults = Impl!(); |
| } |
| |
| /// |
| @safe unittest |
| { |
| int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); |
| static assert(is(ParameterDefaults!foo[0] == void)); |
| static assert( ParameterDefaults!foo[1] == "hello"); |
| static assert( ParameterDefaults!foo[2] == [1,2,3]); |
| static assert( ParameterDefaults!foo[3] == 0); |
| } |
| |
| @safe unittest // issue 17192 |
| { |
| 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_) |
| { |
| } |
| alias Voids = ParameterDefaults!func; |
| static assert(Voids.length == 12); |
| foreach (V; Voids) static assert(is(V == void)); |
| } |
| |
| /** |
| * Alternate name for $(LREF ParameterDefaults), kept for legacy compatibility. |
| */ |
| alias ParameterDefaultValueTuple = ParameterDefaults; |
| |
| @safe unittest |
| { |
| alias PDVT = ParameterDefaultValueTuple; |
| |
| void bar(int n = 1, string s = "hello"){} |
| static assert(PDVT!bar.length == 2); |
| static assert(PDVT!bar[0] == 1); |
| static assert(PDVT!bar[1] == "hello"); |
| static assert(is(typeof(PDVT!bar) == typeof(AliasSeq!(1, "hello")))); |
| |
| void baz(int x, int n = 1, string s = "hello"){} |
| static assert(PDVT!baz.length == 3); |
| static assert(is(PDVT!baz[0] == void)); |
| static assert( PDVT!baz[1] == 1); |
| 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 void foo(int x = 3) { } |
| static assert(PDVT!foo.length == 1); |
| static assert(PDVT!foo[0] == 3); |
| static assert(is(typeof(PDVT!foo) == typeof(AliasSeq!(3)))); |
| |
| struct Colour |
| { |
| ubyte a,r,g,b; |
| |
| static immutable Colour white = Colour(255,255,255,255); |
| } |
| void bug8106(Colour c = Colour.white) {} |
| //pragma(msg, PDVT!bug8106); |
| static assert(PDVT!bug8106[0] == Colour.white); |
| void bug16582(scope int* val = null) {} |
| static assert(PDVT!bug16582[0] is null); |
| } |
| |
| |
| /** |
| Returns the FunctionAttribute mask for function $(D func). |
| |
| See_Also: |
| $(LREF hasFunctionAttributes) |
| */ |
| enum FunctionAttribute : uint |
| { |
| /** |
| * These flags can be bitwise OR-ed together to represent a complex attribute. |
| */ |
| none = 0, |
| pure_ = 1 << 0, /// ditto |
| nothrow_ = 1 << 1, /// ditto |
| ref_ = 1 << 2, /// ditto |
| property = 1 << 3, /// ditto |
| trusted = 1 << 4, /// ditto |
| safe = 1 << 5, /// ditto |
| nogc = 1 << 6, /// ditto |
| system = 1 << 7, /// ditto |
| const_ = 1 << 8, /// ditto |
| immutable_ = 1 << 9, /// ditto |
| inout_ = 1 << 10, /// ditto |
| shared_ = 1 << 11, /// ditto |
| return_ = 1 << 12, /// ditto |
| scope_ = 1 << 13, /// ditto |
| } |
| |
| /// ditto |
| template functionAttributes(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| // @bug: workaround for opCall |
| alias FuncSym = Select!(is(typeof(__traits(getFunctionAttributes, func))), |
| func, Unqual!(FunctionTypeOf!func)); |
| |
| enum FunctionAttribute functionAttributes = |
| extractAttribFlags!(__traits(getFunctionAttributes, FuncSym))(); |
| } |
| |
| /// |
| @safe unittest |
| { |
| import std.traits : functionAttributes, FunctionAttribute; |
| |
| alias FA = FunctionAttribute; // shorten the enum name |
| |
| real func(real x) pure nothrow @safe |
| { |
| return x; |
| } |
| static assert(functionAttributes!func & FA.pure_); |
| static assert(functionAttributes!func & FA.safe); |
| static assert(!(functionAttributes!func & FA.trusted)); // not @trusted |
| } |
| |
| @system unittest |
| { |
| alias FA = FunctionAttribute; |
| |
| struct S |
| { |
| int noF() { return 0; } |
| int constF() const { return 0; } |
| int immutableF() immutable { return 0; } |
| int inoutF() inout { return 0; } |
| int sharedF() shared { return 0; } |
| |
| int x; |
| ref int refF() return { return x; } |
| int propertyF() @property { return 0; } |
| int nothrowF() nothrow { return 0; } |
| int nogcF() @nogc { return 0; } |
| |
| int systemF() @system { return 0; } |
| int trustedF() @trusted { return 0; } |
| int safeF() @safe { return 0; } |
| |
| int pureF() pure { return 0; } |
| } |
| |
| static assert(functionAttributes!(S.noF) == FA.system); |
| static assert(functionAttributes!(typeof(S.noF)) == FA.system); |
| |
| static assert(functionAttributes!(S.constF) == (FA.const_ | FA.system)); |
| static assert(functionAttributes!(typeof(S.constF)) == (FA.const_ | FA.system)); |
| |
| static assert(functionAttributes!(S.immutableF) == (FA.immutable_ | FA.system)); |
| static assert(functionAttributes!(typeof(S.immutableF)) == (FA.immutable_ | FA.system)); |
| |
| static assert(functionAttributes!(S.inoutF) == (FA.inout_ | FA.system)); |
| static assert(functionAttributes!(typeof(S.inoutF)) == (FA.inout_ | FA.system)); |
| |
| static assert(functionAttributes!(S.sharedF) == (FA.shared_ | FA.system)); |
| static assert(functionAttributes!(typeof(S.sharedF)) == (FA.shared_ | FA.system)); |
| |
| static assert(functionAttributes!(S.refF) == (FA.ref_ | FA.system | FA.return_)); |
| static assert(functionAttributes!(typeof(S.refF)) == (FA.ref_ | FA.system | FA.return_)); |
| |
| static assert(functionAttributes!(S.propertyF) == (FA.property | FA.system)); |
| static assert(functionAttributes!(typeof(&S.propertyF)) == (FA.property | FA.system)); |
| |
| static assert(functionAttributes!(S.nothrowF) == (FA.nothrow_ | FA.system)); |
| static assert(functionAttributes!(typeof(S.nothrowF)) == (FA.nothrow_ | FA.system)); |
| |
| static assert(functionAttributes!(S.nogcF) == (FA.nogc | FA.system)); |
| static assert(functionAttributes!(typeof(S.nogcF)) == (FA.nogc | FA.system)); |
| |
| static assert(functionAttributes!(S.systemF) == FA.system); |
| static assert(functionAttributes!(typeof(S.systemF)) == FA.system); |
| |
| static assert(functionAttributes!(S.trustedF) == FA.trusted); |
| static assert(functionAttributes!(typeof(S.trustedF)) == FA.trusted); |
| |
| static assert(functionAttributes!(S.safeF) == FA.safe); |
| static assert(functionAttributes!(typeof(S.safeF)) == FA.safe); |
| |
| static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system)); |
| static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system)); |
| |
| int pure_nothrow() nothrow pure; |
| void safe_nothrow() @safe nothrow; |
| static ref int static_ref_property() @property; |
| ref int ref_property() @property; |
| |
| static assert(functionAttributes!(pure_nothrow) == (FA.pure_ | FA.nothrow_ | FA.system)); |
| static assert(functionAttributes!(typeof(pure_nothrow)) == (FA.pure_ | FA.nothrow_ | FA.system)); |
| |
| static assert(functionAttributes!(safe_nothrow) == (FA.safe | FA.nothrow_)); |
| static assert(functionAttributes!(typeof(safe_nothrow)) == (FA.safe | FA.nothrow_)); |
| |
| static assert(functionAttributes!(static_ref_property) == (FA.property | FA.ref_ | FA.system)); |
| static assert(functionAttributes!(typeof(&static_ref_property)) == (FA.property | FA.ref_ | FA.system)); |
| |
| static assert(functionAttributes!(ref_property) == (FA.property | FA.ref_ | FA.system)); |
| static assert(functionAttributes!(typeof(&ref_property)) == (FA.property | FA.ref_ | FA.system)); |
| |
| struct S2 |
| { |
| int pure_const() const pure { return 0; } |
| int pure_sharedconst() const shared pure { return 0; } |
| } |
| |
| static assert(functionAttributes!(S2.pure_const) == (FA.const_ | FA.pure_ | FA.system)); |
| static assert(functionAttributes!(typeof(S2.pure_const)) == (FA.const_ | FA.pure_ | FA.system)); |
| |
| static assert(functionAttributes!(S2.pure_sharedconst) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system)); |
| static assert(functionAttributes!(typeof(S2.pure_sharedconst)) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system)); |
| |
| static assert(functionAttributes!((int a) { }) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); |
| static assert(functionAttributes!(typeof((int a) { })) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); |
| |
| auto safeDel = delegate() @safe { }; |
| static assert(functionAttributes!(safeDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); |
| static assert(functionAttributes!(typeof(safeDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); |
| |
| auto trustedDel = delegate() @trusted { }; |
| static assert(functionAttributes!(trustedDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted)); |
| static assert(functionAttributes!(typeof(trustedDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted)); |
| |
| auto systemDel = delegate() @system { }; |
| static assert(functionAttributes!(systemDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system)); |
| static assert(functionAttributes!(typeof(systemDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system)); |
| } |
| |
| private FunctionAttribute extractAttribFlags(Attribs...)() |
| { |
| auto res = FunctionAttribute.none; |
| |
| foreach (attrib; Attribs) |
| { |
| switch (attrib) with (FunctionAttribute) |
| { |
| case "pure": res |= pure_; break; |
| case "nothrow": res |= nothrow_; break; |
| case "ref": res |= ref_; break; |
| case "@property": res |= property; break; |
| case "@trusted": res |= trusted; break; |
| case "@safe": res |= safe; break; |
| case "@nogc": res |= nogc; break; |
| case "@system": res |= system; break; |
| case "const": res |= const_; break; |
| case "immutable": res |= immutable_; break; |
| case "inout": res |= inout_; break; |
| case "shared": res |= shared_; break; |
| case "return": res |= return_; break; |
| case "scope": res |= scope_; break; |
| default: assert(0, attrib); |
| } |
| } |
| |
| return res; |
| } |
| |
| /** |
| Checks whether a function has the given attributes attached. |
| |
| Params: |
| args = Function to check, followed by a |
| variadic number of function attributes as strings |
| |
| Returns: |
| `true`, if the function has the list of attributes attached and `false` otherwise. |
| |
| See_Also: |
| $(LREF functionAttributes) |
| */ |
| template hasFunctionAttributes(args...) |
| 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 .. $]) |
| { |
| if (!funcAttribs.canFind(attribute)) |
| return false; |
| } |
| return true; |
| }(); |
| } |
| |
| /// |
| @safe unittest |
| { |
| real func(real x) pure nothrow @safe; |
| static assert(hasFunctionAttributes!(func, "@safe", "pure")); |
| static assert(!hasFunctionAttributes!(func, "@trusted")); |
| |
| // for templates attributes are automatically inferred |
| bool myFunc(T)(T b) |
| { |
| return !b; |
| } |
| static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow")); |
| static assert(!hasFunctionAttributes!(myFunc!bool, "shared")); |
| } |
| |
| @system unittest |
| { |
| struct S |
| { |
| int noF(); |
| int constF() const; |
| int immutableF() immutable; |
| int inoutF() inout; |
| int sharedF() shared; |
| |
| ref int refF() return; |
| int propertyF() @property; |
| int nothrowF() nothrow; |
| int nogcF() @nogc; |
| |
| int systemF() @system; |
| int trustedF() @trusted; |
| int safeF() @safe; |
| |
| int pureF() pure; |
| } |
| |
| // true if no args passed |
| static assert(hasFunctionAttributes!(S.noF)); |
| |
| static assert(hasFunctionAttributes!(S.noF, "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.noF), "@system")); |
| static assert(!hasFunctionAttributes!(S.noF, "@system", "pure")); |
| |
| static assert(hasFunctionAttributes!(S.constF, "const", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.constF), "const", "@system")); |
| static assert(!hasFunctionAttributes!(S.constF, "const", "@system", "@nogc")); |
| |
| static assert(hasFunctionAttributes!(S.immutableF, "immutable", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.immutableF), "immutable", "@system")); |
| static assert(!hasFunctionAttributes!(S.immutableF, "immutable", "@system", "pure")); |
| |
| static assert(hasFunctionAttributes!(S.inoutF, "inout", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.inoutF), "inout", "@system")); |
| static assert(!hasFunctionAttributes!(S.inoutF, "inout", "@system", "pure")); |
| |
| static assert(hasFunctionAttributes!(S.sharedF, "shared", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.sharedF), "shared", "@system")); |
| static assert(!hasFunctionAttributes!(S.sharedF, "shared", "@system", "@trusted")); |
| |
| static assert(hasFunctionAttributes!(S.refF, "ref", "@system", "return")); |
| static assert(hasFunctionAttributes!(typeof(S.refF), "ref", "@system", "return")); |
| static assert(!hasFunctionAttributes!(S.refF, "ref", "@system", "return", "pure")); |
| |
| static assert(hasFunctionAttributes!(S.propertyF, "@property", "@system")); |
| static assert(hasFunctionAttributes!(typeof(&S.propertyF), "@property", "@system")); |
| static assert(!hasFunctionAttributes!(S.propertyF, "@property", "@system", "ref")); |
| |
| static assert(hasFunctionAttributes!(S.nothrowF, "nothrow", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.nothrowF), "nothrow", "@system")); |
| static assert(!hasFunctionAttributes!(S.nothrowF, "nothrow", "@system", "@trusted")); |
| |
| static assert(hasFunctionAttributes!(S.nogcF, "@nogc", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.nogcF), "@nogc", "@system")); |
| static assert(!hasFunctionAttributes!(S.nogcF, "@nogc", "@system", "ref")); |
| |
| static assert(hasFunctionAttributes!(S.systemF, "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.systemF), "@system")); |
| static assert(!hasFunctionAttributes!(S.systemF, "@system", "ref")); |
| |
| static assert(hasFunctionAttributes!(S.trustedF, "@trusted")); |
| static assert(hasFunctionAttributes!(typeof(S.trustedF), "@trusted")); |
| static assert(!hasFunctionAttributes!(S.trustedF, "@trusted", "@safe")); |
| |
| static assert(hasFunctionAttributes!(S.safeF, "@safe")); |
| static assert(hasFunctionAttributes!(typeof(S.safeF), "@safe")); |
| static assert(!hasFunctionAttributes!(S.safeF, "@safe", "nothrow")); |
| |
| static assert(hasFunctionAttributes!(S.pureF, "pure", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S.pureF), "pure", "@system")); |
| static assert(!hasFunctionAttributes!(S.pureF, "pure", "@system", "ref")); |
| |
| int pure_nothrow() nothrow pure { return 0; } |
| void safe_nothrow() @safe nothrow { } |
| static ref int static_ref_property() @property { return *(new int); } |
| ref int ref_property() @property { return *(new int); } |
| |
| static assert(hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe")); |
| static assert(hasFunctionAttributes!(typeof(pure_nothrow), "pure", "nothrow", "@safe")); |
| static assert(!hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe", "@trusted")); |
| |
| static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow")); |
| static assert(hasFunctionAttributes!(typeof(safe_nothrow), "@safe", "nothrow")); |
| static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure")); |
| static assert(!hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure", "@trusted")); |
| |
| static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe")); |
| static assert(hasFunctionAttributes!(typeof(&static_ref_property), "@property", "ref", "@safe")); |
| static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow")); |
| static assert(!hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow", "@nogc")); |
| |
| static assert(hasFunctionAttributes!(ref_property, "@property", "ref", "@safe")); |
| static assert(hasFunctionAttributes!(typeof(&ref_property), "@property", "ref", "@safe")); |
| static assert(!hasFunctionAttributes!(ref_property, "@property", "ref", "@safe", "@nogc")); |
| |
| struct S2 |
| { |
| int pure_const() const pure { return 0; } |
| int pure_sharedconst() const shared pure { return 0; } |
| } |
| |
| static assert(hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S2.pure_const), "const", "pure", "@system")); |
| static assert(!hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system", "ref")); |
| |
| static assert(hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system")); |
| static assert(hasFunctionAttributes!(typeof(S2.pure_sharedconst), "const", "shared", "pure", "@system")); |
| static assert(!hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system", "@nogc")); |
| |
| static assert(hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe")); |
| static assert(hasFunctionAttributes!(typeof((int a) { }), "pure", "nothrow", "@nogc", "@safe")); |
| static assert(!hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe", "ref")); |
| |
| auto safeDel = delegate() @safe { }; |
| static assert(hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe")); |
| static assert(hasFunctionAttributes!(typeof(safeDel), "pure", "nothrow", "@nogc", "@safe")); |
| static assert(!hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe", "@system")); |
| |
| auto trustedDel = delegate() @trusted { }; |
| static assert(hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted")); |
| static assert(hasFunctionAttributes!(typeof(trustedDel), "pure", "nothrow", "@nogc", "@trusted")); |
| static assert(!hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted", "ref")); |
| |
| auto systemDel = delegate() @system { }; |
| static assert(hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system")); |
| static assert(hasFunctionAttributes!(typeof(systemDel), "pure", "nothrow", "@nogc", "@system")); |
| static assert(!hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system", "@property")); |
| |
| |
| // call functions to make CodeCov happy |
| { |
| assert(pure_nothrow == 0); |
| safe_nothrow; |
| assert(static_ref_property == 0); |
| assert(ref_property == 0); |
| assert(S2().pure_const == 0); |
| assert((shared S2()).pure_sharedconst == 0); |
| cast(void) safeDel; |
| cast(void) trustedDel; |
| cast(void) systemDel; |
| } |
| } |
| |
| /** |
| $(D true) if $(D func) is $(D @safe) or $(D @trusted). |
| */ |
| template isSafe(alias func) |
| if (isCallable!func) |
| { |
| enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 || |
| (functionAttributes!func & FunctionAttribute.trusted) != 0; |
| } |
| |
| /// |
| @safe unittest |
| { |
| @safe int add(int a, int b) {return a+b;} |
| @trusted int sub(int a, int b) {return a-b;} |
| @system int mul(int a, int b) {return a*b;} |
| |
| static assert( isSafe!add); |
| static assert( isSafe!sub); |
| static assert(!isSafe!mul); |
| } |
| |
| |
| @safe unittest |
| { |
| //Member functions |
| interface Set |
| { |
| int systemF() @system; |
| int trustedF() @trusted; |
| int safeF() @safe; |
| } |
| static assert( isSafe!(Set.safeF)); |
| static assert( isSafe!(Set.trustedF)); |
| static assert(!isSafe!(Set.systemF)); |
| |
| //Functions |
| @safe static safeFunc() {} |
| @trusted static trustedFunc() {} |
| @system static systemFunc() {} |
| |
| static assert( isSafe!safeFunc); |
| static assert( isSafe!trustedFunc); |
| static assert(!isSafe!systemFunc); |
| |
| //Delegates |
| auto safeDel = delegate() @safe {}; |
| auto trustedDel = delegate() @trusted {}; |
| auto systemDel = delegate() @system {}; |
| |
| static assert( isSafe!safeDel); |
| static assert( isSafe!trustedDel); |
| static assert(!isSafe!systemDel); |
| |
| //Lambdas |
| static assert( isSafe!({safeDel();})); |
| static assert( isSafe!({trustedDel();})); |
| static assert(!isSafe!({systemDel();})); |
| |
| //Static opCall |
| struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } } |
| struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } } |
| struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } } |
| |
| static assert( isSafe!(SafeStatic())); |
| static assert( isSafe!(TrustedStatic())); |
| static assert(!isSafe!(SystemStatic())); |
| |
| //Non-static opCall |
| struct Safe { @safe Safe opCall() { return Safe.init; } } |
| struct Trusted { @trusted Trusted opCall() { return Trusted.init; } } |
| struct System { @system System opCall() { return System.init; } } |
| |
| static assert( isSafe!(Safe.init())); |
| static assert( isSafe!(Trusted.init())); |
| static assert(!isSafe!(System.init())); |
| } |
| |
| |
| /** |
| $(D true) if $(D func) is $(D @system). |
| */ |
| template isUnsafe(alias func) |
| { |
| enum isUnsafe = !isSafe!func; |
| } |
| |
| /// |
| @safe unittest |
| { |
| @safe int add(int a, int b) {return a+b;} |
| @trusted int sub(int a, int b) {return a-b;} |
| @system int mul(int a, int b) {return a*b;} |
| |
| static assert(!isUnsafe!add); |
| static assert(!isUnsafe!sub); |
| static assert( isUnsafe!mul); |
| } |
| |
| @safe unittest |
| { |
| //Member functions |
| interface Set |
| { |
| int systemF() @system; |
| int trustedF() @trusted; |
| int safeF() @safe; |
| } |
| static assert(!isUnsafe!(Set.safeF)); |
| static assert(!isUnsafe!(Set.trustedF)); |
| static assert( isUnsafe!(Set.systemF)); |
| |
| //Functions |
| @safe static safeFunc() {} |
| @trusted static trustedFunc() {} |
| @system static systemFunc() {} |
| |
| static assert(!isUnsafe!safeFunc); |
| static assert(!isUnsafe!trustedFunc); |
| static assert( isUnsafe!systemFunc); |
| |
| //Delegates |
| auto safeDel = delegate() @safe {}; |
| auto trustedDel = delegate() @trusted {}; |
| auto systemDel = delegate() @system {}; |
| |
| static assert(!isUnsafe!safeDel); |
| static assert(!isUnsafe!trustedDel); |
| static assert( isUnsafe!systemDel); |
| |
| //Lambdas |
| static assert(!isUnsafe!({safeDel();})); |
| static assert(!isUnsafe!({trustedDel();})); |
| static assert( isUnsafe!({systemDel();})); |
| |
| //Static opCall |
| struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } } |
| struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } } |
| struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } } |
| |
| static assert(!isUnsafe!(SafeStatic())); |
| static assert(!isUnsafe!(TrustedStatic())); |
| static assert( isUnsafe!(SystemStatic())); |
| |
| //Non-static opCall |
| struct Safe { @safe Safe opCall() { return Safe.init; } } |
| struct Trusted { @trusted Trusted opCall() { return Trusted.init; } } |
| struct System { @system System opCall() { return System.init; } } |
| |
| static assert(!isUnsafe!(Safe.init())); |
| static assert(!isUnsafe!(Trusted.init())); |
| static assert( isUnsafe!(System.init())); |
| } |
| |
| |
| /** |
| 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", "Pascal", or "Objective-C" |
| */ |
| template functionLinkage(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func); |
| } |
| |
| /// |
| @safe unittest |
| { |
| extern(D) void Dfunc() {} |
| extern(C) void Cfunc() {} |
| static assert(functionLinkage!Dfunc == "D"); |
| static assert(functionLinkage!Cfunc == "C"); |
| |
| string a = functionLinkage!Dfunc; |
| assert(a == "D"); |
| |
| auto fp = &Cfunc; |
| string b = functionLinkage!fp; |
| assert(b == "C"); |
| } |
| |
| @safe unittest |
| { |
| interface Test |
| { |
| void const_func() const; |
| void sharedconst_func() shared const; |
| } |
| static assert(functionLinkage!(Test.const_func) == "D"); |
| static assert(functionLinkage!(Test.sharedconst_func) == "D"); |
| |
| static assert(functionLinkage!((int a){}) == "D"); |
| } |
| |
| |
| /** |
| Determines what kind of variadic parameters function has. |
| Params: |
| func = function symbol or type of function, delegate, or pointer to function |
| Returns: |
| enum Variadic |
| */ |
| 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. |
| } |
| |
| /// ditto |
| template variadicFunctionStyle(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func); |
| enum Variadic variadicFunctionStyle = |
| (varargs == "stdarg") ? Variadic.c : |
| (varargs == "argptr") ? Variadic.d : |
| (varargs == "typesafe") ? Variadic.typesafe : |
| (varargs == "none") ? Variadic.no : Variadic.no; |
| } |
| |
| /// |
| @safe unittest |
| { |
| void func() {} |
| static assert(variadicFunctionStyle!func == Variadic.no); |
| |
| extern(C) int printf(in char*, ...); |
| static assert(variadicFunctionStyle!printf == Variadic.c); |
| } |
| |
| @safe unittest |
| { |
| import core.vararg; |
| |
| extern(D) void novar() {} |
| extern(C) void cstyle(int, ...) {} |
| extern(D) void dstyle(...) {} |
| extern(D) void typesafe(int[]...) {} |
| |
| static assert(variadicFunctionStyle!novar == Variadic.no); |
| static assert(variadicFunctionStyle!cstyle == Variadic.c); |
| static assert(variadicFunctionStyle!dstyle == Variadic.d); |
| static assert(variadicFunctionStyle!typesafe == Variadic.typesafe); |
| |
| static assert(variadicFunctionStyle!((int[] a...) {}) == Variadic.typesafe); |
| } |
| |
| |
| /** |
| Get the function type from a callable object $(D func). |
| |
| Using builtin $(D 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. |
| |
| Note: |
| Do not confuse function types with function pointer types; function types are |
| usually used for compile-time reflection purposes. |
| */ |
| template FunctionTypeOf(func...) |
| if (func.length == 1 && isCallable!func) |
| { |
| static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate)) |
| { |
| alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol |
| } |
| else static if (is(typeof(& func[0].opCall) Fobj == delegate)) |
| { |
| alias FunctionTypeOf = Fobj; // HIT: callable object |
| } |
| else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) |
| { |
| alias FunctionTypeOf = Ftyp; // HIT: callable type |
| } |
| else static if (is(func[0] T) || is(typeof(func[0]) T)) |
| { |
| static if (is(T == function)) |
| alias FunctionTypeOf = T; // HIT: function |
| else static if (is(T Fptr : Fptr*) && is(Fptr == function)) |
| alias FunctionTypeOf = Fptr; // HIT: function pointer |
| else static if (is(T Fdlg == delegate)) |
| alias FunctionTypeOf = Fdlg; // HIT: delegate |
| else |
| static assert(0); |
| } |
| else |
| static assert(0); |
| } |
| |
| /// |
| @safe unittest |
| { |
| class C |
| { |
| int value() @property { return 0; } |
| } |
| static assert(is( typeof(C.value) == int )); |
| static assert(is( FunctionTypeOf!(C.value) == function )); |
| } |
| |
| @system unittest |
| { |
| int test(int a); |
| int propGet() @property; |
| int propSet(int a) @property; |
| int function(int) test_fp; |
| int delegate(int) test_dg; |
| static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) )); |
| static assert(is( typeof(test) == FunctionTypeOf!test )); |
| static assert(is( typeof(test) == FunctionTypeOf!test_fp )); |
| static assert(is( typeof(test) == FunctionTypeOf!test_dg )); |
| alias int GetterType() @property; |
| alias int SetterType(int) @property; |
| static assert(is( FunctionTypeOf!propGet == GetterType )); |
| static assert(is( FunctionTypeOf!propSet == SetterType )); |
| |
| interface Prop { int prop() @property; } |
| Prop prop; |
| static assert(is( FunctionTypeOf!(Prop.prop) == GetterType )); |
| static assert(is( FunctionTypeOf!(prop.prop) == GetterType )); |
| |
| class Callable { int opCall(int) { return 0; } } |
| auto call = new Callable; |
| static assert(is( FunctionTypeOf!call == typeof(test) )); |
| |
| struct StaticCallable { static int opCall(int) { return 0; } } |
| StaticCallable stcall_val; |
| StaticCallable* stcall_ptr; |
| static assert(is( FunctionTypeOf!stcall_val == typeof(test) )); |
| static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) )); |
| |
| interface Overloads |
| { |
| void test(string); |
| real test(real); |
| int test(int); |
| int test() @property; |
| } |
| alias ov = AliasSeq!(__traits(getVirtualFunctions, Overloads, "test")); |
| alias F_ov0 = FunctionTypeOf!(ov[0]); |
| alias F_ov1 = FunctionTypeOf!(ov[1]); |
| alias F_ov2 = FunctionTypeOf!(ov[2]); |
| alias F_ov3 = FunctionTypeOf!(ov[3]); |
| static assert(is(F_ov0* == void function(string))); |
| static assert(is(F_ov1* == real function(real))); |
| static assert(is(F_ov2* == int function(int))); |
| static assert(is(F_ov3* == int function() @property)); |
| |
| alias F_dglit = FunctionTypeOf!((int a){ return a; }); |
| static assert(is(F_dglit* : int function(int))); |
| } |
| |
| /** |
| * Constructs a new function or delegate type with the same basic signature |
| * as the given one, but different attributes (including linkage). |
| * |
| * This is especially useful for adding/removing attributes to/from types in |
| * generic code, where the actual type name cannot be spelt out. |
| * |
| * Params: |
| * T = The base type. |
| * linkage = The desired linkage of the result type. |
| * attrs = The desired $(LREF FunctionAttribute)s of the result type. |
| */ |
| template SetFunctionAttributes(T, string linkage, uint attrs) |
| if (isFunctionPointer!T || isDelegate!T) |
| { |
| mixin({ |
| import std.algorithm.searching : canFind; |
| |
| static assert(!(attrs & FunctionAttribute.trusted) || |
| !(attrs & FunctionAttribute.safe), |
| "Cannot have a function/delegate that is both trusted and safe."); |
| |
| static immutable linkages = ["D", "C", "Windows", "Pascal", "C++", "System"]; |
| static assert(canFind(linkages, linkage), "Invalid linkage '" ~ |
| linkage ~ "', must be one of " ~ linkages.stringof ~ "."); |
| |
| string result = "alias "; |
| |
| static if (linkage != "D") |
| result ~= "extern(" ~ linkage ~ ") "; |
| |
| static if (attrs & FunctionAttribute.ref_) |
| result ~= "ref "; |
| |
| result ~= "ReturnType!T"; |
| |
| static if (isDelegate!T) |
| result ~= " delegate"; |
| else |
| result ~= " function"; |
| |
| result ~= "("; |
| |
| static if (Parameters!T.length > 0) |
| result ~= "Parameters!T"; |
| |
| enum varStyle = variadicFunctionStyle!T; |
| static if (varStyle == Variadic.c) |
| result ~= ", ..."; |
| else static if (varStyle == Variadic.d) |
| result ~= "..."; |
| else static if (varStyle == Variadic.typesafe) |
| result ~= "..."; |
| |
| result ~= ")"; |
| |
| static if (attrs & FunctionAttribute.pure_) |
| result ~= " pure"; |
| static if (attrs & FunctionAttribute.nothrow_) |
| result ~= " nothrow"; |
| static if (attrs & FunctionAttribute.property) |
| result ~= " @property"; |
| static if (attrs & FunctionAttribute.trusted) |
| result ~= " @trusted"; |
| static if (attrs & FunctionAttribute.safe) |
| result ~= " @safe"; |
| static if (attrs & FunctionAttribute.nogc) |
| result ~= " @nogc"; |
| static if (attrs & FunctionAttribute.system) |
| result ~= " @system"; |
| static if (attrs & FunctionAttribute.const_) |
| result ~= " const"; |
| static if (attrs & FunctionAttribute.immutable_) |
| result ~= " immutable"; |
| static if (attrs & FunctionAttribute.inout_) |
| result ~= " inout"; |
| static if (attrs & FunctionAttribute.shared_) |
| result ~= " shared"; |
| static if (attrs & FunctionAttribute.return_) |
| result ~= " return"; |
| |
| result ~= " SetFunctionAttributes;"; |
| return result; |
| }()); |
| } |
| |
| /// Ditto |
| template SetFunctionAttributes(T, string linkage, uint attrs) |
| 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 |
| // indirection again. |
| alias SetFunctionAttributes = FunctionTypeOf!(SetFunctionAttributes!(T*, linkage, attrs)); |
| } |
| |
| /// |
| @safe unittest |
| { |
| alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T); |
| |
| auto assumePure(T)(T t) |
| if (isFunctionPointer!T || isDelegate!T) |
| { |
| enum attrs = functionAttributes!T | FunctionAttribute.pure_; |
| return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; |
| } |
| } |
| |
| version (unittest) |
| { |
| // Some function types to test. |
| int sc(scope int, ref int, out int, lazy int, int); |
| extern(System) int novar(); |
| extern(C) int cstyle(int, ...); |
| extern(D) int dstyle(...); |
| extern(D) int typesafe(int[]...); |
| } |
| @safe unittest |
| { |
| import std.algorithm.iteration : reduce; |
| |
| alias FA = FunctionAttribute; |
| 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 |
| enum linkage = functionLinkage!T; |
| enum attrs = functionAttributes!T; |
| |
| static assert(is(SetFunctionAttributes!(T, linkage, attrs) == T), |
| "Identity check failed for: " ~ T.stringof); |
| |
| // Check that all linkage types work (D-style variadics require D linkage). |
| static if (variadicFunctionStyle!T != Variadic.d) |
| { |
| foreach (newLinkage; AliasSeq!("D", "C", "Windows", "Pascal", "C++")) |
| { |
| alias New = SetFunctionAttributes!(T, newLinkage, attrs); |
| static assert(functionLinkage!New == newLinkage, |
| "Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~ |
| " (got " ~ New.stringof ~ ")"); |
| } |
| } |
| |
| // Add @safe. |
| alias T1 = SetFunctionAttributes!(T, functionLinkage!T, FA.safe); |
| static assert(functionAttributes!T1 == FA.safe); |
| |
| // Add all known attributes, excluding conflicting ones. |
| enum allAttrs = reduce!"a | b"([EnumMembers!FA]) |
| & ~FA.safe & ~FA.property & ~FA.const_ & ~FA.immutable_ & ~FA.inout_ |
| & ~FA.shared_ & ~FA.system & ~FA.return_ & ~FA.scope_; |
| |
| alias T2 = SetFunctionAttributes!(T1, functionLinkage!T, allAttrs); |
| static assert(functionAttributes!T2 == allAttrs); |
| |
| // Strip all attributes again. |
| alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none); |
| static assert(is(T3 == T)); |
| }(); |
| } |
| } |
| |
| |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// |
| // Aggregate Types |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// |
| |
| /** |
| Determines whether `T` is a class nested inside another class |
| and that `T.outer` is the implicit reference to the outer class |
| (i.e. `outer` has not been used as a field or method name) |
| |
| Params: |
| T = type to test |
| |
| Returns: |
| `true` if `T` is a class nested inside another, with the conditions described above; |
| `false` otherwise |
| */ |
| template isInnerClass(T) |
| 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); |
| else |
| enum isInnerClass = false; |
| } |
| |
| /// |
| @safe unittest |
| { |
| class C |
| { |
| int outer; |
| } |
| static assert(!isInnerClass!C); |
| |
| class Outer1 |
| { |
| class Inner1 { } |
| class Inner2 |
| { |
| int outer; |
| } |
| } |
| static assert(isInnerClass!(Outer1.Inner1)); |
| static assert(!isInnerClass!(Outer1.Inner2)); |
| |
| static class Outer2 |
| { |
| static class Inner |
| { |
| int outer; |
| } |
| } |
| static assert(!isInnerClass!(Outer2.Inner)); |
| } |
| |
| /** |
| Determines whether $(D T) has its own context pointer. |
| $(D T) must be either $(D class), $(D struct), or $(D union). |
| */ |
| template isNested(T) |
| if (is(T == class) || is(T == struct) || is(T == union)) |
| { |
| enum isNested = __traits(isNested, T); |
| } |
| |
| /// |
| @safe unittest |
| { |
| static struct S { } |
| static assert(!isNested!S); |
| |
| int i; |
| struct NestedStruct { void f() { ++i; } } |
| static assert(isNested!NestedStruct); |
| } |
| |
| /** |
| Determines whether $(D T) or any of its representation types |
| have a context pointer. |
| */ |
| template hasNested(T) |
| { |
| import std.meta : anySatisfy, 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 hasNested = isNested!T || |
| anySatisfy!(.hasNested, Filter!(notSame, Fields!T)); |
| } |
| else |
| enum hasNested = false; |
| } |
| |
| /// |
| @safe unittest |
| { |
| static struct S { } |
| |
| int i; |
| struct NS { void f() { ++i; } } |
| |
| static assert(!hasNested!(S[2])); |
| static assert(hasNested!(NS[2])); |
| } |
| |
| @safe unittest |
| { |
| static assert(!__traits(compiles, isNested!int)); |
| static assert(!hasNested!int); |
| |
| static struct StaticStruct { } |
| static assert(!isNested!StaticStruct); |
| static assert(!hasNested!StaticStruct); |
| |
| int i; |
| struct NestedStruct { void f() { ++i; } } |
| static assert( isNested!NestedStruct); |
| static assert( hasNested!NestedStruct); |
| static assert( isNested!(immutable NestedStruct)); |
| static assert( hasNested!(immutable NestedStruct)); |
| |
| static assert(!__traits(compiles, isNested!(NestedStruct[1]))); |
| static assert( hasNested!(NestedStruct[1])); |
| static assert(!hasNested!(NestedStruct[0])); |
| |
| struct S1 { NestedStruct nested; } |
| static assert(!isNested!S1); |
| static assert( hasNested!S1); |
| |
| static struct S2 { NestedStruct nested; } |
| static assert(!isNested!S2); |
| static assert( hasNested!S2); |
| |
| static struct S3 { NestedStruct[0] nested; } |
| static assert(!isNested!S3); |
| static assert(!hasNested!S3); |
| |
| static union U { NestedStruct nested; } |
| static assert(!isNested!U); |
| static assert( hasNested!U); |
| |
| static class StaticClass { } |
| static assert(!isNested!StaticClass); |
| static assert(!hasNested!StaticClass); |
| |
| class NestedClass { void f() { ++i; } } |
| static assert( isNested!NestedClass); |
| static assert( hasNested!NestedClass); |
| static assert( isNested!(immutable NestedClass)); |
| static assert( hasNested!(immutable NestedClass)); |
| |
| static assert(!__traits(compiles, isNested!(NestedClass[1]))); |
| static assert( hasNested!(NestedClass[1])); |
| static assert(!hasNested!(NestedClass[0])); |
| |
| static class A |
| { |
| A a; |
| } |
| static assert(!hasNested!A); |
| } |
| |
| |
| /*** |
| * Get as a tuple the types 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 a tuple |
| * with one element $(D T). |
| */ |
| 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)) |
| alias Fields = typeof(T.tupleof); |
| else |
| alias Fields = AliasSeq!T; |
| } |
| |
| /// |
| @safe unittest |
| { |
| struct S { int x; float y; } |
| static assert(is(Fields!S == AliasSeq!(int, float))); |
| } |
| |
| /** |
| * Alternate name for $(LREF Fields), kept for legacy compatibility. |
| */ |
| alias FieldTypeTuple = Fields; |
| |
| @safe unittest |
| { |
| static assert(is(FieldTypeTuple!int == AliasSeq!int)); |
| |
| static struct StaticStruct1 { } |
| static assert(is(FieldTypeTuple!StaticStruct1 == AliasSeq!())); |
| |
| static struct StaticStruct2 { int a, b; } |
| static assert(is(FieldTypeTuple!StaticStruct2 == AliasSeq!(int, int))); |
| |
| int i; |
| |
| struct NestedStruct1 { void f() { ++i; } } |
| static assert(is(FieldTypeTuple!NestedStruct1 == AliasSeq!())); |
| |
| struct NestedStruct2 { int a; void f() { ++i; } } |
| static assert(is(FieldTypeTuple!NestedStruct2 == AliasSeq!int)); |
| |
| class NestedClass { int a; void f() { ++i; } } |
| static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int)); |
| } |
| |
| |
| //Required for FieldNameTuple |
| 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. |
| */ |
| 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)) |
| alias FieldNameTuple = staticMap!(NameOf, T.tupleof); |
| else |
| alias FieldNameTuple = AliasSeq!""; |
| } |
| |
| /// |
| @safe unittest |
| { |
| struct S { int x; float y; } |
| static assert(FieldNameTuple!S == AliasSeq!("x", "y")); |
| static assert(FieldNameTuple!int == AliasSeq!""); |
| } |
| |
| @safe unittest |
| { |
| static assert(FieldNameTuple!int == AliasSeq!""); |
| |
| static struct StaticStruct1 { } |
| static assert(is(FieldNameTuple!StaticStruct1 == AliasSeq!())); |
| |
| static struct StaticStruct2 { int a, b; } |
| static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b")); |
| |
| int i; |
| |
| struct NestedStruct1 { void f() { ++i; } } |
| static assert(is(FieldNameTuple!NestedStruct1 == AliasSeq!())); |
| |
| struct NestedStruct2 { int a; void f() { ++i; } } |
| static assert(FieldNameTuple!NestedStruct2 == AliasSeq!"a"); |
| |
| class NestedClass { int a; void f() { ++i; } } |
| static assert(FieldNameTuple!NestedClass == AliasSeq!"a"); |
| } |
| |
| |
| /*** |
| Get the primitive types of the fields of a struct or class, in |
| 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); |
| } |
| else |
| { |
| alias RepresentationTypeTuple = Impl!T; |
| } |
| } |
| |
| /// |
| @safe unittest |
| { |
| struct S1 { int a; float b; } |
| struct S2 { char[] a; union { S1 b; S1 * c; } } |
| alias R = RepresentationTypeTuple!S2; |
| assert(R.length == 4 |
| && is(R[0] == char[]) && is(R[1] == int) |
| && is(R[2] == float) && is(R[3] == S1*)); |
| } |
| |
| @safe unittest |
| { |
| |