| /** | |
| * $(SCRIPT inhibitQuickIndex = 1;) | |
| * $(DIVC quickindex, | |
| * $(BOOKTABLE, | |
| * $(TR $(TH Category) $(TH Symbols)) | |
| * $(TR $(TD Arrays) $(TD | |
| * $(MYREF assumeSafeAppend) | |
| * $(MYREF capacity) | |
| * $(A #.dup.2, $(TT dup)) | |
| * $(MYREF idup) | |
| * $(MYREF reserve) | |
| * )) | |
| * $(TR $(TD Associative arrays) $(TD | |
| * $(MYREF byKey) | |
| * $(MYREF byKeyValue) | |
| * $(MYREF byValue) | |
| * $(MYREF clear) | |
| * $(MYREF dup) | |
| * $(MYREF get) | |
| * $(MYREF keys) | |
| * $(MYREF rehash) | |
| * $(MYREF require) | |
| * $(MYREF update) | |
| * $(MYREF values) | |
| * )) | |
| * $(TR $(TD General) $(TD | |
| * $(MYREF destroy) | |
| * $(MYREF hashOf) | |
| * $(MYREF imported) | |
| * $(MYREF noreturn) | |
| * )) | |
| * $(TR $(TD Classes) $(TD | |
| * $(MYREF Error) | |
| * $(MYREF Exception) | |
| * $(MYREF Object) | |
| * $(MYREF opEquals) | |
| * $(MYREF Throwable) | |
| * )) | |
| * $(TR $(TD Type info) $(TD | |
| * $(MYREF Interface) | |
| * $(MYREF ModuleInfo) | |
| * $(MYREF OffsetTypeInfo) | |
| * $(MYREF RTInfoImpl) | |
| * $(MYREF rtinfoNoPointers) | |
| * $(MYREF TypeInfo) | |
| * $(MYREF TypeInfo_Class) | |
| * )) | |
| * )) | |
| * | |
| * Forms the symbols available to all D programs. Includes Object, which is | |
| * the root of the class object hierarchy. This module is implicitly | |
| * imported. | |
| * | |
| * Copyright: Copyright Digital Mars 2000 - 2011. | |
| * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | |
| * Authors: Walter Bright, Sean Kelly | |
| * Source: $(DRUNTIMESRC object.d) | |
| */ | |
| module object; | |
| alias size_t = typeof(int.sizeof); | |
| alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0); | |
| alias sizediff_t = ptrdiff_t; // For backwards compatibility only. | |
| /** | |
| * Bottom type. | |
| * See $(DDSUBLINK spec/type, noreturn, `noreturn`). | |
| */ | |
| alias noreturn = typeof(*null); | |
| alias hash_t = size_t; // For backwards compatibility only. | |
| alias equals_t = bool; // For backwards compatibility only. | |
| alias string = immutable(char)[]; | |
| alias wstring = immutable(wchar)[]; | |
| alias dstring = immutable(dchar)[]; | |
| version (D_ObjectiveC) | |
| { | |
| deprecated("explicitly import `selector` instead using: `import core.attribute : selector;`") | |
| public import core.attribute : selector; | |
| } | |
| version (Posix) public import core.attribute : gnuAbiTag; | |
| // Some ABIs use a complex varargs implementation requiring TypeInfo.argTypes(). | |
| version (GNU) | |
| { | |
| // No TypeInfo-based core.vararg.va_arg(). | |
| } | |
| else version (X86_64) | |
| { | |
| version (DigitalMars) version = WithArgTypes; | |
| else version (Windows) { /* no need for Win64 ABI */ } | |
| else version = WithArgTypes; | |
| } | |
| else version (AArch64) | |
| { | |
| // Apple uses a trivial varargs implementation | |
| version (DigitalMars) version = WithArgTypes; // is this needed? | |
| else version (OSX) {} | |
| else version (iOS) {} | |
| else version (TVOS) {} | |
| else version (WatchOS) {} | |
| else version = WithArgTypes; | |
| } | |
| /** | |
| * All D class objects inherit from Object. | |
| */ | |
| class Object | |
| { | |
| /** | |
| * Convert Object to a human readable string. | |
| */ | |
| string toString() | |
| { | |
| return typeid(this).name; | |
| } | |
| @system unittest | |
| { | |
| enum unittest_sym_name = __traits(identifier, __traits(parent, (){})); | |
| enum fqn_unittest = "object.Object." ~ unittest_sym_name; // object.__unittest_LX_CY | |
| class C {} | |
| Object obj = new Object; | |
| C c = new C; | |
| assert(obj.toString() == "object.Object"); | |
| assert(c.toString() == fqn_unittest ~ ".C"); | |
| } | |
| /** | |
| * Compute hash function for Object. | |
| */ | |
| size_t toHash() @trusted nothrow | |
| { | |
| // BUG: this prevents a compacting GC from working, needs to be fixed | |
| size_t addr = cast(size_t) cast(void*) this; | |
| // The bottom log2((void*).alignof) bits of the address will always | |
| // be 0. Moreover it is likely that each Object is allocated with a | |
| // separate call to malloc. The alignment of malloc differs from | |
| // platform to platform, but rather than having special cases for | |
| // each platform it is safe to use a shift of 4. To minimize | |
| // collisions in the low bits it is more important for the shift to | |
| // not be too small than for the shift to not be too big. | |
| return addr ^ (addr >>> 4); | |
| } | |
| /** | |
| * Compare with another Object obj. | |
| * Returns: | |
| * $(TABLE | |
| * $(TR $(TD this < obj) $(TD < 0)) | |
| * $(TR $(TD this == obj) $(TD 0)) | |
| * $(TR $(TD this > obj) $(TD > 0)) | |
| * ) | |
| */ | |
| int opCmp(Object o) | |
| { | |
| // BUG: this prevents a compacting GC from working, needs to be fixed | |
| //return cast(int)cast(void*)this - cast(int)cast(void*)o; | |
| throw new Exception("need opCmp for class " ~ typeid(this).name); | |
| //return this !is o; | |
| } | |
| @system unittest | |
| { | |
| Object obj = new Object; | |
| bool gotCaught; | |
| try | |
| { | |
| obj.opCmp(new Object); | |
| } | |
| catch (Exception e) | |
| { | |
| gotCaught = true; | |
| assert(e.msg == "need opCmp for class object.Object"); | |
| } | |
| assert(gotCaught); | |
| } | |
| /** | |
| * Test whether $(D this) is equal to $(D o). | |
| * The default implementation only compares by identity (using the $(D is) operator). | |
| * Generally, overrides and overloads for $(D opEquals) should attempt to compare objects by their contents. | |
| * A class will most likely want to add an overload that takes your specific type as the argument | |
| * and does the content comparison. Then you can override this and forward it to your specific | |
| * typed overload with a cast. Remember to check for `null` on the typed overload. | |
| * | |
| * Examples: | |
| * --- | |
| * class Child { | |
| * int contents; | |
| * // the typed overload first. It can use all the attribute you want | |
| * bool opEquals(const Child c) const @safe pure nothrow @nogc | |
| * { | |
| * if (c is null) | |
| * return false; | |
| * return this.contents == c.contents; | |
| * } | |
| * | |
| * // and now the generic override forwards with a cast | |
| * override bool opEquals(Object o) | |
| * { | |
| * return this.opEquals(cast(Child) o); | |
| * } | |
| * } | |
| * --- | |
| */ | |
| bool opEquals(Object o) | |
| { | |
| return this is o; | |
| } | |
| interface Monitor | |
| { | |
| void lock(); | |
| void unlock(); | |
| } | |
| /** | |
| * Create instance of class specified by the fully qualified name | |
| * classname. | |
| * The class must either have no constructors or have | |
| * a default constructor. | |
| * Returns: | |
| * null if failed | |
| * Example: | |
| * --- | |
| * module foo.bar; | |
| * | |
| * class C | |
| * { | |
| * this() { x = 10; } | |
| * int x; | |
| * } | |
| * | |
| * void main() | |
| * { | |
| * auto c = cast(C)Object.factory("foo.bar.C"); | |
| * assert(c !is null && c.x == 10); | |
| * } | |
| * --- | |
| */ | |
| static Object factory(string classname) | |
| { | |
| auto ci = TypeInfo_Class.find(classname); | |
| if (ci) | |
| { | |
| return ci.create(); | |
| } | |
| return null; | |
| } | |
| @system unittest | |
| { | |
| Object valid_obj = Object.factory("object.Object"); | |
| Object invalid_obj = Object.factory("object.__this_class_doesnt_exist__"); | |
| assert(valid_obj !is null); | |
| assert(invalid_obj is null); | |
| } | |
| } | |
| /++ | |
| Implementation for class opEquals override. Calls the class-defined methods after a null check. | |
| Please note this is not nogc right now, even if your implementation is, because of | |
| the typeinfo name string compare. This is because of dmd's dll implementation. However, | |
| it can infer to @safe if your class' opEquals is. | |
| +/ | |
| bool opEquals(LHS, RHS)(LHS lhs, RHS rhs) | |
| if ((is(LHS : const Object) || is(LHS : const shared Object)) && | |
| (is(RHS : const Object) || is(RHS : const shared Object))) | |
| { | |
| static if (__traits(compiles, lhs.opEquals(rhs)) && __traits(compiles, rhs.opEquals(lhs))) | |
| { | |
| // If aliased to the same object or both null => equal | |
| if (lhs is rhs) return true; | |
| // If either is null => non-equal | |
| if (lhs is null || rhs is null) return false; | |
| if (!lhs.opEquals(rhs)) return false; | |
| // If same exact type => one call to method opEquals | |
| if (typeid(lhs) is typeid(rhs) || | |
| !__ctfe && typeid(lhs).opEquals(typeid(rhs))) | |
| /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't: | |
| https://issues.dlang.org/show_bug.cgi?id=7147 | |
| But CTFE also guarantees that equal TypeInfos are | |
| always identical. So, no opEquals needed during CTFE. */ | |
| { | |
| return true; | |
| } | |
| // General case => symmetric calls to method opEquals | |
| return rhs.opEquals(lhs); | |
| } | |
| else | |
| { | |
| // this is a compatibility hack for the old const cast behavior | |
| // if none of the new overloads compile, we'll go back plain Object, | |
| // including casting away const. It does this through the pointer | |
| // to bypass any opCast that may be present on the original class. | |
| return .opEquals!(Object, Object)(*cast(Object*) &lhs, *cast(Object*) &rhs); | |
| } | |
| } | |
| /// If aliased to the same object or both null => equal | |
| @system unittest // this one is not @safe because it goes through the Object base method | |
| { | |
| class F { int flag; this(int flag) { this.flag = flag; } } | |
| F f; | |
| assert(f == f); // both null | |
| f = new F(1); | |
| assert(f == f); // both aliased to the same object | |
| } | |
| /// If either is null => non-equal | |
| @system unittest | |
| { | |
| class F { int flag; this(int flag) { this.flag = flag; } } | |
| F f; | |
| assert(!(new F(0) == f)); | |
| assert(!(f == new F(0))); | |
| } | |
| /// If same exact type => one call to method opEquals | |
| /// This test passes `@safe` because it defines a new opEquals with `@safe` | |
| @safe unittest | |
| { | |
| class F | |
| { | |
| int flag; | |
| this(int flag) | |
| { | |
| this.flag = flag; | |
| } | |
| bool opEquals(const F o) const @safe nothrow pure | |
| { | |
| return flag == o.flag; | |
| } | |
| } | |
| F f; | |
| assert(new F(0) == new F(0)); | |
| assert(!(new F(0) == new F(1))); | |
| } | |
| /// General case => symmetric calls to method opEquals | |
| @safe unittest | |
| { | |
| int fEquals, gEquals; | |
| class Base | |
| { | |
| int flag; | |
| this(int flag) | |
| { | |
| this.flag = flag; | |
| } | |
| } | |
| class F : Base | |
| { | |
| this(int flag) { super(flag); } | |
| bool opEquals(const Base o) @safe | |
| { | |
| fEquals++; | |
| return flag == o.flag; | |
| } | |
| } | |
| class G : Base | |
| { | |
| this(int flag) { super(flag); } | |
| bool opEquals(const Base o) @safe | |
| { | |
| gEquals++; | |
| return flag == o.flag; | |
| } | |
| } | |
| assert(new F(1) == new G(1)); | |
| assert(fEquals == 1); | |
| assert(gEquals == 1); | |
| } | |
| /++ | |
| This test shows an example for a comprehensive inheritance equality chain too. | |
| +/ | |
| unittest | |
| { | |
| static class Base | |
| { | |
| int member; | |
| this(int member) pure @safe nothrow @nogc | |
| { | |
| this.member = member; | |
| } | |
| override bool opEquals(Object rhs) const | |
| { | |
| return this.opEquals(cast(Base) rhs); | |
| } | |
| bool opEquals(const Base rhs) const @nogc pure nothrow @safe | |
| { | |
| if (rhs is null) | |
| return false; | |
| return this.member == rhs.member; | |
| } | |
| } | |
| // works through the direct class with attributes enabled, except for pure and nogc in the current TypeInfo implementation | |
| bool testThroughBase() nothrow @safe | |
| { | |
| Base b1 = new Base(0); | |
| Base b2 = new Base(0); | |
| assert(b1 == b2); | |
| Base b3 = new Base(1); | |
| assert(b1 != b3); | |
| return true; | |
| } | |
| static assert(testThroughBase()); | |
| // also works through the base class interface thanks to the override, but no more attributes | |
| bool testThroughObject() | |
| { | |
| Object o1 = new Base(0); | |
| Object o2 = new Base(0); | |
| assert(o1 == o2); | |
| Object o3 = new Base(1); | |
| assert(o1 != o3); | |
| return true; | |
| } | |
| static assert(testThroughObject()); | |
| // Each time you make a child, you want to override all old opEquals | |
| // and add a new overload for the new child. | |
| static class Child : Base | |
| { | |
| int member2; | |
| this(int member, int member2) pure @safe nothrow @nogc | |
| { | |
| super(member); | |
| this.member2 = member2; | |
| } | |
| // override the whole chain so it works consistently though any base | |
| override bool opEquals(Object rhs) const | |
| { | |
| return this.opEquals(cast(Child) rhs); | |
| } | |
| override bool opEquals(const Base rhs) const | |
| { | |
| return this.opEquals(cast(const Child) rhs); | |
| } | |
| // and then add the new overload, if necessary, to handle new members | |
| bool opEquals(const Child rhs) const @nogc pure nothrow @safe | |
| { | |
| if (rhs is null) | |
| return false; | |
| // can call back to the devirtualized base test with implicit conversion | |
| // then compare the new member too. or we could have just compared the base | |
| // member directly here as well. | |
| return Base.opEquals(rhs) && this.member2 == rhs.member2; | |
| } | |
| // a mixin template, of course, could automate this. | |
| } | |
| bool testThroughChild() | |
| { | |
| Child a = new Child(0, 0); | |
| Child b = new Child(0, 1); | |
| assert(a != b); | |
| Base ba = a; | |
| Base bb = b; | |
| assert(ba != bb); | |
| Object oa = a; | |
| Object ob = b; | |
| assert(oa != ob); | |
| return true; | |
| } | |
| static assert(testThroughChild()); | |
| } | |
| // To cover const Object opEquals | |
| @system unittest | |
| { | |
| const Object obj1 = new Object; | |
| const Object obj2 = new Object; | |
| assert(obj1 == obj1); | |
| assert(obj1 != obj2); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=23291 | |
| @system unittest | |
| { | |
| import core.atomic : atomicLoad; | |
| static shared class C { bool opEquals(const(shared(C)) rhs) const shared { return true;}} | |
| const(C) c = new C(); | |
| const(C)[] a = [c]; | |
| const(C)[] b = [c]; | |
| // Call the shared-aware overload directly to avoid `==` introducing | |
| // additional shared reads during lowering. | |
| assert(atomicLoad(a[0]).opEquals(atomicLoad(b[0]))); | |
| } | |
| private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow; | |
| /** Makes ownee use owner's mutex. | |
| * This will initialize owner's mutex if it hasn't been set yet. | |
| * Params: | |
| * ownee = object to change | |
| * owner = source object | |
| */ | |
| void setSameMutex(shared Object ownee, shared Object owner) | |
| { | |
| import core.atomic : atomicLoad; | |
| _d_setSameMutex(atomicLoad(ownee), atomicLoad(owner)); | |
| } | |
| @system unittest | |
| { | |
| import core.atomic : atomicLoad; | |
| shared Object obj1 = new Object; | |
| synchronized class C | |
| { | |
| void bar() {} | |
| } | |
| shared C obj2 = new shared(C); | |
| obj2.bar(); | |
| assert(obj1.__monitor != obj2.__monitor); | |
| assert(obj1.__monitor is null); | |
| setSameMutex(atomicLoad(obj1), atomicLoad(obj2)); | |
| assert(obj1.__monitor == obj2.__monitor); | |
| assert(obj1.__monitor !is null); | |
| } | |
| /** | |
| * Information about an interface. | |
| * When an object is accessed via an interface, an Interface* appears as the | |
| * first entry in its vtbl. | |
| */ | |
| struct Interface | |
| { | |
| /// Class info returned by `typeid` for this interface (not for containing class) | |
| TypeInfo_Class classinfo; | |
| void*[] vtbl; | |
| size_t offset; /// offset to Interface 'this' from Object 'this' | |
| } | |
| /** | |
| * Array of pairs giving the offset and type information for each | |
| * member in an aggregate. | |
| */ | |
| struct OffsetTypeInfo | |
| { | |
| size_t offset; /// Offset of member from start of object | |
| TypeInfo ti; /// TypeInfo for this member | |
| } | |
| /** | |
| * Runtime type information about a type. | |
| * Can be retrieved for any type using a | |
| * $(GLINK2 expression,TypeidExpression, TypeidExpression). | |
| */ | |
| class TypeInfo | |
| { | |
| override string toString() const @safe nothrow | |
| { | |
| return typeid(this).name; | |
| } | |
| override size_t toHash() @trusted const nothrow | |
| { | |
| return hashOf(this.toString()); | |
| } | |
| override int opCmp(Object rhs) | |
| { | |
| if (this is rhs) | |
| return 0; | |
| auto ti = cast(TypeInfo) rhs; | |
| if (ti is null) | |
| return 1; | |
| return __cmp(this.toString(), ti.toString()); | |
| } | |
| @system unittest | |
| { | |
| assert(typeid(void) <= typeid(void)); | |
| assert(typeid(void).opCmp(null)); | |
| assert(!typeid(void).opCmp(typeid(void))); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| return opEquals(cast(TypeInfo) o); | |
| } | |
| bool opEquals(const TypeInfo ti) @safe nothrow const | |
| { | |
| /* TypeInfo instances are singletons, but duplicates can exist | |
| * across DLL's. Therefore, comparing for a name match is | |
| * sufficient. | |
| */ | |
| if (this is ti) | |
| return true; | |
| return ti && this.toString() == ti.toString(); | |
| } | |
| @system unittest | |
| { | |
| auto anotherObj = new Object(); | |
| assert(typeid(void).opEquals(typeid(void))); | |
| assert(typeid(void) != anotherObj); // calling .opEquals here directly is a type mismatch | |
| } | |
| /** | |
| * Computes a hash of the instance of a type. | |
| * Params: | |
| * p = pointer to start of instance of the type | |
| * Returns: | |
| * the hash | |
| * Bugs: | |
| * fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface. | |
| */ | |
| size_t getHash(scope const void* p) @trusted nothrow const | |
| { | |
| // by default, do not assume anything about the type | |
| return 0; | |
| } | |
| /// Compares two instances for equality. | |
| bool equals(in void* p1, in void* p2) const { return p1 == p2; } | |
| /// Compares two instances for <, ==, or >. | |
| int compare(in void* p1, in void* p2) const { return _xopCmp(p1, p2); } | |
| /// Returns size of the type. | |
| @property size_t tsize() nothrow pure const @safe @nogc { return 0; } | |
| /// Swaps two instances of the type. | |
| void swap(void* p1, void* p2) const | |
| { | |
| size_t remaining = tsize; | |
| // If the type might contain pointers perform the swap in pointer-sized | |
| // chunks in case a garbage collection pass interrupts this function. | |
| if ((cast(size_t) p1 | cast(size_t) p2) % (void*).alignof == 0) | |
| { | |
| while (remaining >= (void*).sizeof) | |
| { | |
| void* tmp = *cast(void**) p1; | |
| *cast(void**) p1 = *cast(void**) p2; | |
| *cast(void**) p2 = tmp; | |
| p1 += (void*).sizeof; | |
| p2 += (void*).sizeof; | |
| remaining -= (void*).sizeof; | |
| } | |
| } | |
| for (size_t i = 0; i < remaining; i++) | |
| { | |
| byte t = (cast(byte *)p1)[i]; | |
| (cast(byte*)p1)[i] = (cast(byte*)p2)[i]; | |
| (cast(byte*)p2)[i] = t; | |
| } | |
| } | |
| @system unittest | |
| { | |
| class _TypeInfo_Dummy : TypeInfo | |
| { | |
| override const(void)[] initializer() const { return []; } | |
| @property override size_t tsize() nothrow pure const @safe @nogc { return tsize_val; } | |
| size_t tsize_val; | |
| } | |
| auto dummy = new _TypeInfo_Dummy(); | |
| cast(void)dummy.initializer(); // For coverage completeness | |
| int a = 2, b = -2; | |
| dummy.swap(&a, &b); | |
| // does nothing because tsize is 0 | |
| assert(a == 2); | |
| assert(b == -2); | |
| dummy.tsize_val = int.sizeof; | |
| dummy.swap(&a, &b); | |
| assert(a == -2); | |
| assert(b == 2); | |
| void* ptr_a = null, ptr_b = cast(void*)1; | |
| dummy.tsize_val = (void*).sizeof; | |
| dummy.swap(&ptr_a, &ptr_b); | |
| assert(ptr_a is cast(void*)1); | |
| assert(ptr_b is null); | |
| } | |
| /** Get TypeInfo for 'next' type, as defined by what kind of type this is, | |
| null if none. */ | |
| @property inout(TypeInfo) next() nothrow pure inout @nogc { return null; } | |
| /** | |
| * Return default initializer. If the type should be initialized to all | |
| * zeros, an array with a null ptr and a length equal to the type size will | |
| * be returned. For static arrays, this returns the default initializer for | |
| * a single element of the array, use `tsize` to get the correct size. | |
| */ | |
| abstract const(void)[] initializer() nothrow pure const @safe @nogc; | |
| /** Get flags for type: 1 means GC should scan for pointers, | |
| 2 means arg of this type is passed in SIMD register(s) if available */ | |
| @property uint flags() nothrow pure const @safe @nogc { return 0; } | |
| /// Get type information on the contents of the type; null if not available | |
| const(OffsetTypeInfo)[] offTi() const { return null; } | |
| /// Run the destructor on the object and all its sub-objects | |
| void destroy(void* p) const {} | |
| /// Run the postblit on the object and all its sub-objects | |
| void postblit(void* p) const {} | |
| /// Return alignment of type | |
| @property size_t talign() nothrow pure const @safe @nogc { return tsize; } | |
| /** Return internal info on arguments fitting into 8byte. | |
| * See X86-64 ABI 3.2.3 | |
| */ | |
| version (WithArgTypes) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow | |
| { | |
| arg1 = this; | |
| return 0; | |
| } | |
| /** Return info used by the garbage collector to do precise collection. | |
| */ | |
| @property immutable(void)* rtInfo() nothrow pure const @trusted @nogc { return rtinfoHasPointers; } // better safe than sorry | |
| } | |
| @system unittest | |
| { | |
| class _TypeInfo_Dummy : TypeInfo | |
| { | |
| override const(void)[] initializer() const { return []; } | |
| } | |
| auto dummy = new _TypeInfo_Dummy(); | |
| cast(void)dummy.initializer(); // For coverage completeness | |
| assert(dummy.rtInfo() is rtinfoHasPointers); | |
| assert(typeid(void).rtInfo() is rtinfoNoPointers); | |
| assert(dummy.tsize() == 0); | |
| bool gotCaught; | |
| try | |
| { | |
| dummy.compare(null, null); | |
| } catch (Error e) | |
| { | |
| gotCaught = true; | |
| assert(e.msg == "TypeInfo.compare is not implemented"); | |
| } | |
| assert(gotCaught); | |
| assert(dummy.equals(null, null)); | |
| assert(!dummy.equals(cast(void*)1, null)); | |
| } | |
| @system unittest | |
| { | |
| assert(typeid(void).next() is null); | |
| assert(typeid(void).offTi() is null); | |
| assert(typeid(void).tsize() == 1); | |
| version (WithArgTypes) | |
| { | |
| TypeInfo ti1; | |
| TypeInfo ti2; | |
| assert(typeid(void).argTypes(ti1, ti2) == 0); | |
| assert(typeid(void) is ti1); | |
| assert(ti1 !is null); | |
| assert(ti2 is null); | |
| } | |
| } | |
| @system unittest | |
| { | |
| class _ZypeInfo_Dummy : TypeInfo | |
| { | |
| override const(void)[] initializer() const { return []; } | |
| } | |
| auto dummy2 = new _ZypeInfo_Dummy(); | |
| cast(void)dummy2.initializer(); // For coverage completeness | |
| assert(typeid(void) > dummy2); | |
| assert(dummy2 < typeid(void)); | |
| } | |
| @safe unittest | |
| { | |
| enum unittest_sym_name = __traits(identifier, __traits(parent, (){})); | |
| enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY | |
| class _TypeInfo_Dummy : TypeInfo | |
| { | |
| override const(void)[] initializer() const { return []; } | |
| } | |
| auto dummy = new _TypeInfo_Dummy(); | |
| cast(void)dummy.initializer(); // For coverage completeness | |
| assert(dummy.toString() == fqn_unittest ~ "._TypeInfo_Dummy"); | |
| assert(dummy.toHash() == hashOf(dummy.toString())); | |
| assert(dummy.getHash(null) == 0); | |
| } | |
| class TypeInfo_Enum : TypeInfo | |
| { | |
| override string toString() const pure { return name; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Enum)o; | |
| return c && this.name == c.name && | |
| this.base == c.base; | |
| } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| enum EE { A, B, C } | |
| assert(typeid(E).opEquals(typeid(E))); | |
| assert(!typeid(E).opEquals(typeid(EE))); | |
| } | |
| override size_t getHash(scope const void* p) const { return base.getHash(p); } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| E e1 = E.A; | |
| E e2 = E.B; | |
| assert(typeid(E).getHash(&e1) == hashOf(E.A)); | |
| assert(typeid(E).getHash(&e2) == hashOf(E.B)); | |
| enum ES : string { A = "foo", B = "bar" } | |
| ES es1 = ES.A; | |
| ES es2 = ES.B; | |
| assert(typeid(ES).getHash(&es1) == hashOf("foo")); | |
| assert(typeid(ES).getHash(&es2) == hashOf("bar")); | |
| } | |
| override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| E e1 = E.A; | |
| E e2 = E.B; | |
| assert(typeid(E).equals(&e1, &e1)); | |
| assert(!typeid(E).equals(&e1, &e2)); | |
| } | |
| override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| E e1 = E.A; | |
| E e2 = E.B; | |
| assert(typeid(E).compare(&e1, &e1) == 0); | |
| assert(typeid(E).compare(&e1, &e2) < 0); | |
| assert(typeid(E).compare(&e2, &e1) > 0); | |
| } | |
| override @property size_t tsize() nothrow pure const { return base.tsize; } | |
| @safe unittest | |
| { | |
| enum E { A, B, C } | |
| enum ES : string { A = "a", B = "b", C = "c"} | |
| assert(typeid(E).tsize == E.sizeof); | |
| assert(typeid(ES).tsize == ES.sizeof); | |
| assert(typeid(E).tsize != ES.sizeof); | |
| } | |
| override void swap(void* p1, void* p2) const { return base.swap(p1, p2); } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| E e1 = E.A; | |
| E e2 = E.B; | |
| typeid(E).swap(&e1, &e2); | |
| assert(e1 == E.B); | |
| assert(e2 == E.A); | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| assert(typeid(E).next is null); | |
| } | |
| override @property uint flags() nothrow pure const { return base.flags; } | |
| @safe unittest | |
| { | |
| enum E { A, B, C } | |
| assert(typeid(E).flags == 0); | |
| } | |
| override const(OffsetTypeInfo)[] offTi() const { return base.offTi; } | |
| @system unittest | |
| { | |
| enum E { A, B, C } | |
| assert(typeid(E).offTi is null); | |
| } | |
| override void destroy(void* p) const { return base.destroy(p); } | |
| override void postblit(void* p) const { return base.postblit(p); } | |
| override const(void)[] initializer() const | |
| { | |
| return m_init.length ? m_init : base.initializer(); | |
| } | |
| override @property size_t talign() nothrow pure const { return base.talign; } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| return base.argTypes(arg1, arg2); | |
| } | |
| override @property immutable(void)* rtInfo() const { return base.rtInfo; } | |
| TypeInfo base; | |
| string name; | |
| void[] m_init; | |
| } | |
| @safe unittest | |
| { | |
| enum unittest_sym_name = __traits(identifier, __traits(parent, (){})); | |
| enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY | |
| enum E { A, B, C } | |
| enum EE { A, B, C } | |
| assert(typeid(E).toString() == fqn_unittest ~ ".E"); | |
| } | |
| @safe unittest // https://issues.dlang.org/show_bug.cgi?id=12233 | |
| { | |
| static assert(is(typeof(TypeInfo.init) == TypeInfo)); | |
| assert(TypeInfo.init is null); | |
| } | |
| // Please make sure to keep this in sync with TypeInfo_P (src/rt/typeinfo/ti_ptr.d) | |
| class TypeInfo_Pointer : TypeInfo | |
| { | |
| override string toString() const { return m_next.toString() ~ "*"; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Pointer)o; | |
| return c && this.m_next == c.m_next; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| size_t addr = cast(size_t) *cast(const void**)p; | |
| return addr ^ (addr >> 4); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| return *cast(void**)p1 == *cast(void**)p2; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| const v1 = *cast(void**) p1, v2 = *cast(void**) p2; | |
| return (v1 > v2) - (v1 < v2); | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return (void*).sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (void*).sizeof]; | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| void* tmp = *cast(void**)p1; | |
| *cast(void**)p1 = *cast(void**)p2; | |
| *cast(void**)p2 = tmp; | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return m_next; } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo m_next; | |
| } | |
| class TypeInfo_Array : TypeInfo | |
| { | |
| override string toString() const { return value.toString() ~ "[]"; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Array)o; | |
| return c && this.value == c.value; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| void[] a = *cast(void[]*)p; | |
| return getArrayHash(value, a.ptr, a.length); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| void[] a1 = *cast(void[]*)p1; | |
| void[] a2 = *cast(void[]*)p2; | |
| if (a1.length != a2.length) | |
| return false; | |
| size_t sz = value.tsize; | |
| for (size_t i = 0; i < a1.length; i++) | |
| { | |
| if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| void[] a1 = *cast(void[]*)p1; | |
| void[] a2 = *cast(void[]*)p2; | |
| size_t sz = value.tsize; | |
| size_t len = a1.length; | |
| if (a2.length < len) | |
| len = a2.length; | |
| for (size_t u = 0; u < len; u++) | |
| { | |
| immutable int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz); | |
| if (result) | |
| return result; | |
| } | |
| return (a1.length > a2.length) - (a1.length < a2.length); | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return (void[]).sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (void[]).sizeof]; | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| void[] tmp = *cast(void[]*)p1; | |
| *cast(void[]*)p1 = *cast(void[]*)p2; | |
| *cast(void[]*)p2 = tmp; | |
| } | |
| TypeInfo value; | |
| override @property inout(TypeInfo) next() nothrow pure inout | |
| { | |
| return value; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| override @property size_t talign() nothrow pure const | |
| { | |
| return (void[]).alignof; | |
| } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(size_t); | |
| arg2 = typeid(void*); | |
| return 0; | |
| } | |
| override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(void[]); } | |
| } | |
| class TypeInfo_StaticArray : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| import core.internal.string : unsignedToTempString; | |
| char[20] tmpBuff = void; | |
| const lenString = unsignedToTempString(len, tmpBuff); | |
| return (() @trusted => cast(string) (value.toString() ~ "[" ~ lenString ~ "]"))(); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_StaticArray)o; | |
| return c && this.len == c.len && | |
| this.value == c.value; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| return getArrayHash(value, p, len); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| size_t sz = value.tsize; | |
| for (size_t u = 0; u < len; u++) | |
| { | |
| if (!value.equals(p1 + u * sz, p2 + u * sz)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| size_t sz = value.tsize; | |
| for (size_t u = 0; u < len; u++) | |
| { | |
| immutable int result = value.compare(p1 + u * sz, p2 + u * sz); | |
| if (result) | |
| return result; | |
| } | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return len * value.tsize; | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| import core.stdc.string : memcpy; | |
| size_t remaining = value.tsize * len; | |
| void[size_t.sizeof * 4] buffer = void; | |
| while (remaining > buffer.length) | |
| { | |
| memcpy(buffer.ptr, p1, buffer.length); | |
| memcpy(p1, p2, buffer.length); | |
| memcpy(p2, buffer.ptr, buffer.length); | |
| p1 += buffer.length; | |
| p2 += buffer.length; | |
| remaining -= buffer.length; | |
| } | |
| memcpy(buffer.ptr, p1, remaining); | |
| memcpy(p1, p2, remaining); | |
| memcpy(p2, buffer.ptr, remaining); | |
| } | |
| override const(void)[] initializer() nothrow pure const | |
| { | |
| return value.initializer(); | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return value; } | |
| override @property uint flags() nothrow pure const { return value.flags; } | |
| override void destroy(void* p) const | |
| { | |
| immutable sz = value.tsize; | |
| p += sz * len; | |
| foreach (i; 0 .. len) | |
| { | |
| p -= sz; | |
| value.destroy(p); | |
| } | |
| } | |
| override void postblit(void* p) const | |
| { | |
| immutable sz = value.tsize; | |
| foreach (i; 0 .. len) | |
| { | |
| value.postblit(p); | |
| p += sz; | |
| } | |
| } | |
| TypeInfo value; | |
| size_t len; | |
| override @property size_t talign() nothrow pure const | |
| { | |
| return value.talign; | |
| } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(void*); | |
| return 0; | |
| } | |
| // just return the rtInfo of the element, we have no generic type T to run RTInfo!T on | |
| override @property immutable(void)* rtInfo() nothrow pure const @safe { return value.rtInfo(); } | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=21315 | |
| @system unittest | |
| { | |
| int[16] a, b; | |
| foreach (int i; 0 .. 16) | |
| { | |
| a[i] = i; | |
| b[i] = ~i; | |
| } | |
| typeid(int[16]).swap(&a, &b); | |
| foreach (int i; 0 .. 16) | |
| { | |
| assert(a[i] == ~i); | |
| assert(b[i] == i); | |
| } | |
| } | |
| class TypeInfo_AssociativeArray : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| return value.toString() ~ "[" ~ key.toString() ~ "]"; | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_AssociativeArray)o; | |
| return c && this.key == c.key && | |
| this.value == c.value; | |
| } | |
| override bool equals(in void* p1, in void* p2) @trusted const | |
| { | |
| return xopEquals(p1, p2); | |
| } | |
| override hash_t getHash(scope const void* p) nothrow @trusted const | |
| { | |
| return xtoHash(p); | |
| } | |
| // BUG: need to add the rest of the functions | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return (char[int]).sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (char[int]).sizeof]; | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return value; } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| // TypeInfo entry is generated from the type of this template to help rt/aaA.d | |
| private static import core.internal.newaa; | |
| alias Entry(K, V) = core.internal.newaa.Entry!(K, V); | |
| TypeInfo value; | |
| TypeInfo key; | |
| TypeInfo entry; | |
| bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals; | |
| hash_t function(scope const void*) nothrow @safe xtoHash; | |
| alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V); | |
| alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V); | |
| override @property size_t talign() nothrow pure const | |
| { | |
| return (char[int]).alignof; | |
| } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(void*); | |
| return 0; | |
| } | |
| } | |
| class TypeInfo_Vector : TypeInfo | |
| { | |
| override string toString() const { return "__vector(" ~ base.toString() ~ ")"; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Vector)o; | |
| return c && this.base == c.base; | |
| } | |
| override size_t getHash(scope const void* p) const { return base.getHash(p); } | |
| override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); } | |
| override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); } | |
| override @property size_t tsize() nothrow pure const { return base.tsize; } | |
| override void swap(void* p1, void* p2) const { return base.swap(p1, p2); } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } | |
| override @property uint flags() nothrow pure const { return 2; /* passed in SIMD register */ } | |
| override const(void)[] initializer() nothrow pure const | |
| { | |
| return base.initializer(); | |
| } | |
| override @property size_t talign() nothrow pure const { return 16; } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| return base.argTypes(arg1, arg2); | |
| } | |
| TypeInfo base; | |
| } | |
| class TypeInfo_Function : TypeInfo | |
| { | |
| override string toString() const pure @trusted | |
| { | |
| import core.demangle : demangleType; | |
| alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure; | |
| SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType; | |
| return cast(string) demangle(deco); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Function)o; | |
| return c && this.deco == c.deco; | |
| } | |
| // BUG: need to add the rest of the functions | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return 0; // no size for functions | |
| } | |
| override const(void)[] initializer() const @safe | |
| { | |
| return null; | |
| } | |
| override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; } | |
| TypeInfo next; | |
| /** | |
| * Mangled function type string | |
| */ | |
| string deco; | |
| } | |
| @safe unittest | |
| { | |
| abstract class C | |
| { | |
| void func(); | |
| void func(int a); | |
| int func(int a, int b); | |
| } | |
| alias functionTypes = typeof(__traits(getVirtualMethods, C, "func")); | |
| assert(typeid(functionTypes[0]).toString() == "void function()"); | |
| assert(typeid(functionTypes[1]).toString() == "void function(int)"); | |
| assert(typeid(functionTypes[2]).toString() == "int function(int, int)"); | |
| } | |
| @system unittest | |
| { | |
| abstract class C | |
| { | |
| void func(); | |
| void func(int a); | |
| } | |
| alias functionTypes = typeof(__traits(getVirtualMethods, C, "func")); | |
| Object obj = typeid(functionTypes[0]); | |
| assert(obj.opEquals(typeid(functionTypes[0]))); | |
| assert(typeid(functionTypes[0]) == typeid(functionTypes[0])); | |
| assert(typeid(functionTypes[0]) != typeid(functionTypes[1])); | |
| assert(typeid(functionTypes[0]).tsize() == 0); | |
| assert(typeid(functionTypes[0]).initializer() is null); | |
| assert(typeid(functionTypes[0]).rtInfo() is null); | |
| } | |
| class TypeInfo_Delegate : TypeInfo | |
| { | |
| override string toString() const pure @trusted | |
| { | |
| import core.demangle : demangleType; | |
| alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure; | |
| SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType; | |
| return cast(string) demangle(deco); | |
| } | |
| @safe unittest | |
| { | |
| double sqr(double x) { return x * x; } | |
| sqr(double.init); // for coverage completeness | |
| auto delegate_str = "double delegate(double) pure nothrow @nogc @safe"; | |
| assert(typeid(typeof(&sqr)).toString() == delegate_str); | |
| assert(delegate_str.hashOf() == typeid(typeof(&sqr)).hashOf()); | |
| assert(typeid(typeof(&sqr)).toHash() == typeid(typeof(&sqr)).hashOf()); | |
| int g; | |
| alias delegate_type = typeof((int a, int b) => a + b + g); | |
| delegate_str = "int delegate(int, int) pure nothrow @nogc @safe"; | |
| assert(typeid(delegate_type).toString() == delegate_str); | |
| assert(delegate_str.hashOf() == typeid(delegate_type).hashOf()); | |
| assert(typeid(delegate_type).toHash() == typeid(delegate_type).hashOf()); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Delegate)o; | |
| return c && this.deco == c.deco; | |
| } | |
| @system unittest | |
| { | |
| double sqr(double x) { return x * x; } | |
| int dbl(int x) { return x + x; } | |
| sqr(double.init); // for coverage completeness | |
| dbl(int.init); // for coverage completeness | |
| Object obj = typeid(typeof(&sqr)); | |
| assert(obj.opEquals(typeid(typeof(&sqr)))); | |
| assert(typeid(typeof(&sqr)) == typeid(typeof(&sqr))); | |
| assert(typeid(typeof(&dbl)) != typeid(typeof(&sqr))); | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| return hashOf(*cast(const void delegate() *)p); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| auto dg1 = *cast(void delegate()*)p1; | |
| auto dg2 = *cast(void delegate()*)p2; | |
| return dg1 == dg2; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| auto dg1 = *cast(void delegate()*)p1; | |
| auto dg2 = *cast(void delegate()*)p2; | |
| if (dg1 < dg2) | |
| return -1; | |
| else if (dg1 > dg2) | |
| return 1; | |
| else | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| alias dg = int delegate(); | |
| return dg.sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (int delegate()).sizeof]; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo next; | |
| string deco; | |
| override @property size_t talign() nothrow pure const | |
| { | |
| alias dg = int delegate(); | |
| return dg.alignof; | |
| } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(void*); | |
| arg2 = typeid(void*); | |
| return 0; | |
| } | |
| override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(int delegate()); } | |
| } | |
| private extern (C) Object _d_newclass(const TypeInfo_Class ci); | |
| extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @nogc nothrow pure @safe | |
| { | |
| import core.internal.cast_ : areClassInfosEqual; | |
| if (areClassInfosEqual(oc, c)) | |
| return true; | |
| do | |
| { | |
| if (oc.base && areClassInfosEqual(oc.base, c)) | |
| return true; | |
| // Bugzilla 2013: Use depth-first search to calculate offset | |
| // from the derived (oc) to the base (c). | |
| foreach (iface; oc.interfaces) | |
| { | |
| if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c)) | |
| return true; | |
| } | |
| oc = oc.base; | |
| } while (oc); | |
| return false; | |
| } | |
| /****************************************** | |
| * Given a pointer: | |
| * If it is an Object, return that Object. | |
| * If it is an interface, return the Object implementing the interface. | |
| * If it is null, return null. | |
| * Else, undefined crash | |
| */ | |
| extern(C) Object _d_toObject(return scope void* p) @nogc nothrow pure @trusted | |
| { | |
| if (!p) | |
| return null; | |
| Object o = cast(Object) p; | |
| Interface* pi = **cast(Interface***) p; | |
| /* Interface.offset lines up with ClassInfo.name.ptr, | |
| * so we rely on pointers never being less than 64K, | |
| * and Objects never being greater. | |
| */ | |
| if (pi.offset < 0x10000) | |
| return cast(Object)(p - pi.offset); | |
| return o; | |
| } | |
| /** | |
| * Runtime type information about a class. | |
| * Can be retrieved from an object instance by using the | |
| * $(DDSUBLINK spec/expression,typeid_expressions,typeid expression). | |
| */ | |
| class TypeInfo_Class : TypeInfo | |
| { | |
| override string toString() const pure { return name; } | |
| override bool opEquals(const TypeInfo o) const | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Class)o; | |
| return c && this.name == c.name; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| auto o = *cast(Object*)p; | |
| return o ? o.toHash() : 0; | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| Object o1 = *cast(Object*)p1; | |
| Object o2 = *cast(Object*)p2; | |
| return (o1 is o2) || (o1 && o1.opEquals(o2)); | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| Object o1 = *cast(Object*)p1; | |
| Object o2 = *cast(Object*)p2; | |
| int c = 0; | |
| // Regard null references as always being "less than" | |
| if (o1 !is o2) | |
| { | |
| if (o1) | |
| { | |
| if (!o2) | |
| c = 1; | |
| else | |
| c = o1.opCmp(o2); | |
| } | |
| else | |
| c = -1; | |
| } | |
| return c; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return Object.sizeof; | |
| } | |
| override const(void)[] initializer() nothrow pure const @safe | |
| { | |
| return m_init; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| override @property const(OffsetTypeInfo)[] offTi() nothrow pure const | |
| { | |
| return m_offTi; | |
| } | |
| final @property auto info() @safe @nogc nothrow pure const return { return this; } | |
| final @property auto typeinfo() @safe @nogc nothrow pure const return { return this; } | |
| byte[] m_init; /** class static initializer | |
| * (init.length gives size in bytes of class) | |
| */ | |
| string name; /// class name | |
| void*[] vtbl; /// virtual function pointer table | |
| Interface[] interfaces; /// interfaces this class implements | |
| TypeInfo_Class base; /// base class | |
| void* destructor; | |
| void function(Object) classInvariant; | |
| enum ClassFlags : ushort | |
| { | |
| isCOMclass = 0x1, | |
| noPointers = 0x2, | |
| hasOffTi = 0x4, | |
| hasCtor = 0x8, | |
| hasGetMembers = 0x10, | |
| hasTypeInfo = 0x20, | |
| isAbstract = 0x40, | |
| isCPPclass = 0x80, | |
| hasDtor = 0x100, | |
| hasNameSig = 0x200, | |
| } | |
| ClassFlags m_flags; | |
| ushort depth; /// inheritance distance from Object | |
| void* deallocator; | |
| OffsetTypeInfo[] m_offTi; | |
| void function(Object) defaultConstructor; // default Constructor | |
| immutable(void)* m_RTInfo; // data for precise GC | |
| override @property immutable(void)* rtInfo() const { return m_RTInfo; } | |
| uint[4] nameSig; /// unique signature for `name` | |
| /** | |
| * Search all modules for TypeInfo_Class corresponding to classname. | |
| * Returns: null if not found | |
| */ | |
| static const(TypeInfo_Class) find(const scope char[] classname) | |
| { | |
| foreach (m; ModuleInfo) | |
| { | |
| if (m) | |
| { | |
| //writefln("module %s, %d", m.name, m.localClasses.length); | |
| foreach (c; m.localClasses) | |
| { | |
| if (c is null) | |
| continue; | |
| //writefln("\tclass %s", c.name); | |
| if (c.name == classname) | |
| return c; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Create instance of Object represented by 'this'. | |
| */ | |
| Object create() const | |
| { | |
| if (m_flags & 8 && !defaultConstructor) | |
| return null; | |
| if (m_flags & 64) // abstract | |
| return null; | |
| Object o = _d_newclass(this); | |
| if (m_flags & 8 && defaultConstructor) | |
| { | |
| defaultConstructor(o); | |
| } | |
| return o; | |
| } | |
| /** | |
| * Returns true if the class described by `child` derives from or is | |
| * the class described by this `TypeInfo_Class`. Always returns false | |
| * if the argument is null. | |
| * | |
| * Params: | |
| * child = TypeInfo for some class | |
| * Returns: | |
| * true if the class described by `child` derives from or is the | |
| * class described by this `TypeInfo_Class`. | |
| */ | |
| final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted | |
| { | |
| if (m_init.length) | |
| { | |
| // If this TypeInfo_Class represents an actual class we only need | |
| // to check the child and its direct ancestors. | |
| for (auto ti = cast() child; ti !is null; ti = ti.base) | |
| if (ti is this) | |
| return true; | |
| return false; | |
| } | |
| else | |
| { | |
| // If this TypeInfo_Class is the .info field of a TypeInfo_Interface | |
| // we also need to recursively check the child's interfaces. | |
| return child !is null && _d_isbaseof(cast() child, this); | |
| } | |
| } | |
| } | |
| alias ClassInfo = TypeInfo_Class; | |
| @safe unittest | |
| { | |
| // Bugzilla 14401 | |
| static class X | |
| { | |
| int a; | |
| } | |
| assert(typeid(X).initializer is typeid(X).m_init); | |
| assert(typeid(X).initializer.length == typeid(const(X)).initializer.length); | |
| assert(typeid(X).initializer.length == typeid(shared(X)).initializer.length); | |
| assert(typeid(X).initializer.length == typeid(immutable(X)).initializer.length); | |
| } | |
| class TypeInfo_Interface : TypeInfo | |
| { | |
| override string toString() const pure { return info.name; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Interface)o; | |
| return c && this.info.name == typeid(c).name; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| if (!*cast(void**)p) | |
| { | |
| return 0; | |
| } | |
| Interface* pi = **cast(Interface ***)*cast(void**)p; | |
| Object o = cast(Object)(*cast(void**)p - pi.offset); | |
| assert(o); | |
| return o.toHash(); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| Interface* pi = **cast(Interface ***)*cast(void**)p1; | |
| Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); | |
| pi = **cast(Interface ***)*cast(void**)p2; | |
| Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); | |
| return o1 == o2 || (o1 && o1.opCmp(o2) == 0); | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| Interface* pi = **cast(Interface ***)*cast(void**)p1; | |
| Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); | |
| pi = **cast(Interface ***)*cast(void**)p2; | |
| Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); | |
| int c = 0; | |
| // Regard null references as always being "less than" | |
| if (o1 != o2) | |
| { | |
| if (o1) | |
| { | |
| if (!o2) | |
| c = 1; | |
| else | |
| c = o1.opCmp(o2); | |
| } | |
| else | |
| c = -1; | |
| } | |
| return c; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return Object.sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. Object.sizeof]; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo_Class info; | |
| /** | |
| * Returns true if the class described by `child` derives from the | |
| * interface described by this `TypeInfo_Interface`. Always returns | |
| * false if the argument is null. | |
| * | |
| * Params: | |
| * child = TypeInfo for some class | |
| * Returns: | |
| * true if the class described by `child` derives from the | |
| * interface described by this `TypeInfo_Interface`. | |
| */ | |
| final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted | |
| { | |
| return child !is null && _d_isbaseof(cast() child, this.info); | |
| } | |
| /** | |
| * Returns true if the interface described by `child` derives from | |
| * or is the interface described by this `TypeInfo_Interface`. | |
| * Always returns false if the argument is null. | |
| * | |
| * Params: | |
| * child = TypeInfo for some interface | |
| * Returns: | |
| * true if the interface described by `child` derives from or is | |
| * the interface described by this `TypeInfo_Interface`. | |
| */ | |
| final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted | |
| { | |
| return child !is null && _d_isbaseof(cast() child.info, this.info); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| enum unittest_sym_name = __traits(identifier, __traits(parent, (){})); | |
| enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY | |
| interface I {} | |
| assert(fqn_unittest ~ ".I" == typeid(I).info.name); | |
| assert((fqn_unittest ~ ".I").hashOf() == typeid(I).hashOf()); | |
| assert(typeid(I).toHash() == typeid(I).hashOf()); | |
| } | |
| class TypeInfo_Struct : TypeInfo | |
| { | |
| override string toString() const { return name; } | |
| override size_t toHash() const | |
| { | |
| return hashOf(this.mangledName); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto s = cast(const TypeInfo_Struct)o; | |
| return s && this.mangledName == s.mangledName; | |
| } | |
| override size_t getHash(scope const void* p) @trusted pure nothrow const | |
| { | |
| assert(p); | |
| if (xtoHash) | |
| { | |
| return (*xtoHash)(p); | |
| } | |
| else | |
| { | |
| return hashOf(p[0 .. initializer().length]); | |
| } | |
| } | |
| override bool equals(in void* p1, in void* p2) @trusted pure nothrow const | |
| { | |
| import core.stdc.string : memcmp; | |
| if (!p1 || !p2) | |
| return false; | |
| else if (xopEquals) | |
| { | |
| const dg = _memberFunc(p1, xopEquals); | |
| return dg.xopEquals(p2); | |
| } | |
| else if (p1 == p2) | |
| return true; | |
| else | |
| // BUG: relies on the GC not moving objects | |
| return memcmp(p1, p2, initializer().length) == 0; | |
| } | |
| override int compare(in void* p1, in void* p2) @trusted pure nothrow const | |
| { | |
| import core.stdc.string : memcmp; | |
| // Regard null references as always being "less than" | |
| if (p1 != p2) | |
| { | |
| if (p1) | |
| { | |
| if (!p2) | |
| return true; | |
| else if (xopCmp) | |
| { | |
| const dg = _memberFunc(p1, xopCmp); | |
| return dg.xopCmp(p2); | |
| } | |
| else | |
| // BUG: relies on the GC not moving objects | |
| return memcmp(p1, p2, initializer().length); | |
| } | |
| else | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return initializer().length; | |
| } | |
| override const(void)[] initializer() nothrow pure const @safe | |
| { | |
| return m_init; | |
| } | |
| override @property uint flags() nothrow pure const { return m_flags; } | |
| override @property size_t talign() nothrow pure const { return m_align; } | |
| final override void destroy(void* p) const | |
| { | |
| if (xdtor) | |
| { | |
| if (m_flags & StructFlags.isDynamicType) | |
| (*xdtorti)(p, this); | |
| else | |
| (*xdtor)(p); | |
| } | |
| } | |
| override void postblit(void* p) const | |
| { | |
| if (xpostblit) | |
| (*xpostblit)(p); | |
| } | |
| string mangledName; | |
| final @property string name() nothrow const @trusted | |
| { | |
| import core.demangle : demangleType; | |
| if (mangledName is null) // e.g., opaque structs | |
| return null; | |
| const key = cast(const void*) this; // faster lookup than TypeInfo_Struct, at the cost of potential duplicates per binary | |
| static string[typeof(key)] demangledNamesCache; // per thread | |
| // not nothrow: | |
| //return demangledNamesCache.require(key, cast(string) demangleType(mangledName)); | |
| if (auto pDemangled = key in demangledNamesCache) | |
| return *pDemangled; | |
| const demangled = cast(string) demangleType(mangledName); | |
| demangledNamesCache[key] = demangled; | |
| return demangled; | |
| } | |
| void[] m_init; // initializer; m_init.ptr == null if 0 initialize | |
| @safe pure nothrow | |
| { | |
| size_t function(in void*) xtoHash; | |
| bool function(in void*, in void*) xopEquals; | |
| int function(in void*, in void*) xopCmp; | |
| string function(in void*) xtoString; | |
| enum StructFlags : uint | |
| { | |
| hasPointers = 0x1, | |
| isDynamicType = 0x2, // built at runtime, needs type info in xdtor | |
| } | |
| StructFlags m_flags; | |
| } | |
| union | |
| { | |
| void function(void*) xdtor; | |
| void function(void*, const TypeInfo_Struct ti) xdtorti; | |
| } | |
| void function(void*) xpostblit; | |
| uint m_align; | |
| override @property immutable(void)* rtInfo() nothrow pure const @safe { return m_RTInfo; } | |
| version (WithArgTypes) | |
| { | |
| override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = m_arg1; | |
| arg2 = m_arg2; | |
| return 0; | |
| } | |
| TypeInfo m_arg1; | |
| TypeInfo m_arg2; | |
| } | |
| immutable(void)* m_RTInfo; // data for precise GC | |
| // The xopEquals and xopCmp members are function pointers to member | |
| // functions, which is not guaranteed to share the same ABI, as it is not | |
| // known whether the `this` parameter is the first or second argument. | |
| // This wrapper is to convert it to a delegate which will always pass the | |
| // `this` parameter in the correct way. | |
| private struct _memberFunc | |
| { | |
| union | |
| { | |
| struct // delegate | |
| { | |
| const void* ptr; | |
| const void* funcptr; | |
| } | |
| @safe pure nothrow | |
| { | |
| bool delegate(in void*) xopEquals; | |
| int delegate(in void*) xopCmp; | |
| } | |
| } | |
| } | |
| } | |
| @system unittest | |
| { | |
| struct S | |
| { | |
| bool opEquals(ref const S rhs) const | |
| { | |
| return false; | |
| } | |
| } | |
| S s; | |
| assert(!typeid(S).equals(&s, &s)); | |
| } | |
| class TypeInfo_Tuple : TypeInfo | |
| { | |
| TypeInfo[] elements; | |
| override string toString() const | |
| { | |
| string s = "("; | |
| foreach (i, element; elements) | |
| { | |
| if (i) | |
| s ~= ','; | |
| s ~= element.toString(); | |
| } | |
| s ~= ")"; | |
| return s; | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto t = cast(const TypeInfo_Tuple)o; | |
| if (t && elements.length == t.elements.length) | |
| { | |
| for (size_t i = 0; i < elements.length; i++) | |
| { | |
| if (elements[i] != t.elements[i]) | |
| return false; | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| override size_t getHash(scope const void* p) const | |
| { | |
| assert(0); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| assert(0); | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| assert(0); | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| assert(0); | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| assert(0); | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| assert(0); | |
| } | |
| override void destroy(void* p) const | |
| { | |
| assert(0); | |
| } | |
| override void postblit(void* p) const | |
| { | |
| assert(0); | |
| } | |
| override @property size_t talign() nothrow pure const | |
| { | |
| assert(0); | |
| } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| assert(0); | |
| } | |
| } | |
| class TypeInfo_Const : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("const(" ~ base.toString() ~ ")"); | |
| } | |
| //override bool opEquals(Object o) { return base.opEquals(o); } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| if (typeid(this) != typeid(o)) | |
| return false; | |
| auto t = cast(TypeInfo_Const)o; | |
| return base.opEquals(t.base); | |
| } | |
| override size_t getHash(scope const void *p) const { return base.getHash(p); } | |
| override bool equals(in void *p1, in void *p2) const { return base.equals(p1, p2); } | |
| override int compare(in void *p1, in void *p2) const { return base.compare(p1, p2); } | |
| override @property size_t tsize() nothrow pure const { return base.tsize; } | |
| override void swap(void *p1, void *p2) const { return base.swap(p1, p2); } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } | |
| override @property uint flags() nothrow pure const { return base.flags; } | |
| override const(void)[] initializer() nothrow pure const | |
| { | |
| return base.initializer(); | |
| } | |
| override @property size_t talign() nothrow pure const { return base.talign; } | |
| version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| return base.argTypes(arg1, arg2); | |
| } | |
| TypeInfo base; | |
| } | |
| class TypeInfo_Invariant : TypeInfo_Const | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("immutable(" ~ base.toString() ~ ")"); | |
| } | |
| } | |
| class TypeInfo_Shared : TypeInfo_Const | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("shared(" ~ base.toString() ~ ")"); | |
| } | |
| } | |
| class TypeInfo_Inout : TypeInfo_Const | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("inout(" ~ base.toString() ~ ")"); | |
| } | |
| } | |
| // Contents of Moduleinfo._flags | |
| enum | |
| { | |
| MIctorstart = 0x1, // we've started constructing it | |
| MIctordone = 0x2, // finished construction | |
| MIstandalone = 0x4, // module ctor does not depend on other module | |
| // ctors being done first | |
| MItlsctor = 8, | |
| MItlsdtor = 0x10, | |
| MIctor = 0x20, | |
| MIdtor = 0x40, | |
| MIxgetMembers = 0x80, | |
| MIictor = 0x100, | |
| MIunitTest = 0x200, | |
| MIimportedModules = 0x400, | |
| MIlocalClasses = 0x800, | |
| MIname = 0x1000, | |
| } | |
| /***************************************** | |
| * An instance of ModuleInfo is generated into the object file for each compiled module. | |
| * | |
| * It provides access to various aspects of the module. | |
| * It is not generated for betterC. | |
| */ | |
| struct ModuleInfo | |
| { | |
| uint _flags; // MIxxxx | |
| uint _index; // index into _moduleinfo_array[] | |
| version (all) | |
| { | |
| deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.") | |
| void opAssign(const scope ModuleInfo m) { _flags = m._flags; _index = m._index; } | |
| } | |
| else | |
| { | |
| @disable this(); | |
| } | |
| const: | |
| private void* addrOf(int flag) return nothrow pure @nogc | |
| in | |
| { | |
| assert(flag >= MItlsctor && flag <= MIname); | |
| assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1)); | |
| } | |
| do | |
| { | |
| import core.stdc.string : strlen; | |
| void* p = cast(void*)&this + ModuleInfo.sizeof; | |
| if (flags & MItlsctor) | |
| { | |
| if (flag == MItlsctor) return p; | |
| p += typeof(tlsctor).sizeof; | |
| } | |
| if (flags & MItlsdtor) | |
| { | |
| if (flag == MItlsdtor) return p; | |
| p += typeof(tlsdtor).sizeof; | |
| } | |
| if (flags & MIctor) | |
| { | |
| if (flag == MIctor) return p; | |
| p += typeof(ctor).sizeof; | |
| } | |
| if (flags & MIdtor) | |
| { | |
| if (flag == MIdtor) return p; | |
| p += typeof(dtor).sizeof; | |
| } | |
| if (flags & MIxgetMembers) | |
| { | |
| if (flag == MIxgetMembers) return p; | |
| p += typeof(xgetMembers).sizeof; | |
| } | |
| if (flags & MIictor) | |
| { | |
| if (flag == MIictor) return p; | |
| p += typeof(ictor).sizeof; | |
| } | |
| if (flags & MIunitTest) | |
| { | |
| if (flag == MIunitTest) return p; | |
| p += typeof(unitTest).sizeof; | |
| } | |
| if (flags & MIimportedModules) | |
| { | |
| if (flag == MIimportedModules) return p; | |
| p += size_t.sizeof + *cast(size_t*)p * typeof(importedModules[0]).sizeof; | |
| } | |
| if (flags & MIlocalClasses) | |
| { | |
| if (flag == MIlocalClasses) return p; | |
| p += size_t.sizeof + *cast(size_t*)p * typeof(localClasses[0]).sizeof; | |
| } | |
| if (true || flags & MIname) // always available for now | |
| { | |
| if (flag == MIname) return p; | |
| p += strlen(cast(immutable char*)p); | |
| } | |
| assert(0); | |
| } | |
| @property uint index() nothrow pure @nogc { return _index; } | |
| @property uint flags() nothrow pure @nogc { return _flags; } | |
| /************************ | |
| * Returns: | |
| * module constructor for thread locals, `null` if there isn't one | |
| */ | |
| @property void function() tlsctor() nothrow pure @nogc | |
| { | |
| return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null; | |
| } | |
| /************************ | |
| * Returns: | |
| * module destructor for thread locals, `null` if there isn't one | |
| */ | |
| @property void function() tlsdtor() nothrow pure @nogc | |
| { | |
| return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null; | |
| } | |
| /***************************** | |
| * Returns: | |
| * address of a module's `const(MemberInfo)[] getMembers(string)` function, `null` if there isn't one | |
| */ | |
| @property void* xgetMembers() nothrow pure @nogc | |
| { | |
| return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null; | |
| } | |
| /************************ | |
| * Returns: | |
| * module constructor, `null` if there isn't one | |
| */ | |
| @property void function() ctor() nothrow pure @nogc | |
| { | |
| return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null; | |
| } | |
| /************************ | |
| * Returns: | |
| * module destructor, `null` if there isn't one | |
| */ | |
| @property void function() dtor() nothrow pure @nogc | |
| { | |
| return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null; | |
| } | |
| /************************ | |
| * Returns: | |
| * module order independent constructor, `null` if there isn't one | |
| */ | |
| @property void function() ictor() nothrow pure @nogc | |
| { | |
| return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null; | |
| } | |
| /************* | |
| * Returns: | |
| * address of function that runs the module's unittests, `null` if there isn't one | |
| */ | |
| @property void function() unitTest() nothrow pure @nogc | |
| { | |
| return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null; | |
| } | |
| /**************** | |
| * Returns: | |
| * array of pointers to the ModuleInfo's of modules imported by this one | |
| */ | |
| @property immutable(ModuleInfo*)[] importedModules() return nothrow pure @nogc | |
| { | |
| if (flags & MIimportedModules) | |
| { | |
| auto p = cast(size_t*)addrOf(MIimportedModules); | |
| return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p]; | |
| } | |
| return null; | |
| } | |
| /**************** | |
| * Returns: | |
| * array of TypeInfo_Class references for classes defined in this module | |
| */ | |
| @property TypeInfo_Class[] localClasses() return nothrow pure @nogc | |
| { | |
| if (flags & MIlocalClasses) | |
| { | |
| auto p = cast(size_t*)addrOf(MIlocalClasses); | |
| return (cast(TypeInfo_Class*)(p + 1))[0 .. *p]; | |
| } | |
| return null; | |
| } | |
| /******************** | |
| * Returns: | |
| * name of module, `null` if no name | |
| */ | |
| @property string name() return nothrow pure @nogc | |
| { | |
| import core.stdc.string : strlen; | |
| auto p = cast(immutable char*) addrOf(MIname); | |
| return p[0 .. strlen(p)]; | |
| } | |
| static int opApply(scope int delegate(ModuleInfo*) dg) | |
| { | |
| import core.internal.traits : externDFunc; | |
| alias moduleinfos_apply = externDFunc!("rt.minfo.moduleinfos_apply", | |
| int function(scope int delegate(immutable(ModuleInfo*)))); | |
| // Bugzilla 13084 - enforcing immutable ModuleInfo would break client code | |
| return moduleinfos_apply( | |
| (immutable(ModuleInfo*)m) => dg(cast(ModuleInfo*)m)); | |
| } | |
| } | |
| @system unittest | |
| { | |
| ModuleInfo* m1; | |
| foreach (m; ModuleInfo) | |
| { | |
| m1 = m; | |
| } | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // Throwable | |
| /////////////////////////////////////////////////////////////////////////////// | |
| /** | |
| * The base class of all thrown objects. | |
| * | |
| * All thrown objects must inherit from Throwable. Class $(D Exception), which | |
| * derives from this class, represents the category of thrown objects that are | |
| * safe to catch and handle. In principle, one should not catch Throwable | |
| * objects that are not derived from $(D Exception), as they represent | |
| * unrecoverable runtime errors. Certain runtime guarantees may fail to hold | |
| * when these errors are thrown, making it unsafe to continue execution after | |
| * catching them. | |
| */ | |
| class Throwable : Object | |
| { | |
| interface TraceInfo | |
| { | |
| int opApply(scope int delegate(ref const(char[]))) const; | |
| int opApply(scope int delegate(ref size_t, ref const(char[]))) const; | |
| string toString() const; | |
| } | |
| alias TraceDeallocator = void function(TraceInfo) nothrow; | |
| string msg; /// A message describing the error. | |
| /** | |
| * The _file name of the D source code corresponding with | |
| * where the error was thrown from. | |
| */ | |
| string file; | |
| /** | |
| * The _line number of the D source code corresponding with | |
| * where the error was thrown from. | |
| */ | |
| size_t line; | |
| /** | |
| * The stack trace of where the error happened. This is an opaque object | |
| * that can either be converted to $(D string), or iterated over with $(D | |
| * foreach) to extract the items in the stack trace (as strings). | |
| */ | |
| TraceInfo info; | |
| /** | |
| * If set, this is used to deallocate the TraceInfo on destruction. | |
| */ | |
| TraceDeallocator infoDeallocator; | |
| /** | |
| * A reference to the _next error in the list. This is used when a new | |
| * $(D Throwable) is thrown from inside a $(D catch) block. The originally | |
| * caught $(D Exception) will be chained to the new $(D Throwable) via this | |
| * field. | |
| * | |
| * If the 0 bit is set in this, it means the next exception is refcounted, | |
| * meaning this object owns that object and should destroy it on | |
| * destruction. | |
| * | |
| * WARNING: This means we are storing an interior pointer to the next in | |
| * chain, to signify that the next in the chain is actually reference | |
| * counted. We rely on the fact that a pointer to a Throwable is not 1-byte | |
| * aligned. It is important to use a local field to indicate reference | |
| * counting since we are not allowed to look at GC-allocated references | |
| * inside the destructor. It is very important to store this as a void*, | |
| * since optimizers can consider an unaligned pointer to not exist. | |
| */ | |
| private void* _nextInChainPtr; | |
| private @property bool _nextIsRefcounted() @trusted scope pure nothrow @nogc const | |
| { | |
| if (__ctfe) | |
| return false; | |
| return (cast(size_t)_nextInChainPtr) & 1; | |
| } | |
| private uint _refcount; // 0 : allocated by GC | |
| // 1 : allocated by _d_newThrowable() | |
| // 2.. : reference count + 1 | |
| /** | |
| * Returns: | |
| * A reference to the _next error in the list. This is used when a new | |
| * $(D Throwable) is thrown from inside a $(D catch) block. The originally | |
| * caught $(D Exception) will be chained to the new $(D Throwable) via this | |
| * field. | |
| */ | |
| @property inout(Throwable) next() @trusted inout return scope pure nothrow @nogc | |
| { | |
| if (__ctfe) | |
| return cast(inout(Throwable)) _nextInChainPtr; | |
| return cast(inout(Throwable)) (_nextInChainPtr - _nextIsRefcounted); | |
| } | |
| /** | |
| * Replace next in chain with `tail`. | |
| * Use `chainTogether` instead if at all possible. | |
| */ | |
| @property void next(Throwable tail) @trusted scope pure nothrow @nogc | |
| { | |
| void* newTail = cast(void*)tail; | |
| if (tail && tail._refcount) | |
| { | |
| ++tail._refcount; // increment the replacement *first* | |
| ++newTail; // indicate ref counting is used | |
| } | |
| auto n = next; | |
| auto nrc = _nextIsRefcounted; | |
| _nextInChainPtr = null; // sever the tail before deleting it | |
| if (nrc) | |
| _d_delThrowable(n); // now delete the old tail | |
| _nextInChainPtr = newTail; // and set the new tail | |
| } | |
| /** | |
| * Returns: | |
| * mutable reference to the reference count, which is | |
| * 0 - allocated by the GC, 1 - allocated by _d_newThrowable(), | |
| * and >=2 which is the reference count + 1 | |
| * Note: | |
| * Marked as `@system` to discourage casual use of it. | |
| */ | |
| @system @nogc final pure nothrow ref uint refcount() return { return _refcount; } | |
| /** | |
| * Loop over the chain of Throwables. | |
| */ | |
| int opApply(scope int delegate(Throwable) dg) | |
| { | |
| int result = 0; | |
| for (Throwable t = this; t; t = t.next) | |
| { | |
| result = dg(t); | |
| if (result) | |
| break; | |
| } | |
| return result; | |
| } | |
| /** | |
| * Append `e2` to chain of exceptions that starts with `e1`. | |
| * Params: | |
| * e1 = start of chain (can be null) | |
| * e2 = second part of chain (can be null) | |
| * Returns: | |
| * Throwable that is at the start of the chain; null if both `e1` and `e2` are null | |
| */ | |
| static @__future @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2) | |
| { | |
| if (!e1) | |
| return e2; | |
| if (!e2) | |
| return e1; | |
| for (auto e = e1; 1; e = e.next) | |
| { | |
| if (!e.next) | |
| { | |
| e.next = e2; | |
| break; | |
| } | |
| } | |
| return e1; | |
| } | |
| @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null) | |
| { | |
| this.msg = msg; | |
| this.next = nextInChain; | |
| //this.info = _d_traceContext(); | |
| } | |
| @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null) | |
| { | |
| this(msg, nextInChain); | |
| this.file = file; | |
| this.line = line; | |
| //this.info = _d_traceContext(); | |
| } | |
| @trusted nothrow ~this() | |
| { | |
| if (_nextIsRefcounted) | |
| _d_delThrowable(next); | |
| // handle owned traceinfo | |
| if (infoDeallocator !is null) | |
| { | |
| infoDeallocator(info); | |
| info = null; // avoid any kind of dangling pointers if we can help | |
| // it. | |
| } | |
| } | |
| /** | |
| * Overrides $(D Object.toString) and returns the error message. | |
| * Internally this forwards to the $(D toString) overload that | |
| * takes a $(D_PARAM sink) delegate. | |
| */ | |
| override string toString() | |
| { | |
| string s; | |
| toString((in buf) { s ~= buf; }); | |
| return s; | |
| } | |
| /** | |
| * The Throwable hierarchy uses a toString overload that takes a | |
| * $(D_PARAM _sink) delegate to avoid GC allocations, which cannot be | |
| * performed in certain error situations. Override this $(D | |
| * toString) method to customize the error message. | |
| */ | |
| void toString(scope void delegate(in char[]) sink) const | |
| { | |
| import core.internal.string : unsignedToTempString; | |
| char[20] tmpBuff = void; | |
| sink(typeid(this).name); | |
| sink("@"); sink(file); | |
| sink("("); sink(unsignedToTempString(line, tmpBuff)); sink(")"); | |
| if (msg.length) | |
| { | |
| sink(": "); sink(msg); | |
| } | |
| if (info) | |
| { | |
| try | |
| { | |
| sink("\n----------------"); | |
| foreach (t; info) | |
| { | |
| sink("\n"); sink(t); | |
| } | |
| } | |
| catch (Throwable) | |
| { | |
| // ignore more errors | |
| } | |
| } | |
| } | |
| /** | |
| * Get the message describing the error. | |
| * | |
| * This getter is an alternative way to access the Exception's message, | |
| * with the added advantage of being override-able in subclasses. | |
| * Subclasses are hence free to do their own memory managements without | |
| * being tied to the requirement of providing a `string` in a field. | |
| * | |
| * The default behavior is to return the `Throwable.msg` field. | |
| * | |
| * Returns: | |
| * A message representing the cause of the `Throwable` | |
| */ | |
| @__future const(char)[] message() const @safe nothrow | |
| { | |
| return this.msg; | |
| } | |
| } | |
| /** | |
| * The base class of all errors that are safe to catch and handle. | |
| * | |
| * In principle, only thrown objects derived from this class are safe to catch | |
| * inside a $(D catch) block. Thrown objects not derived from Exception | |
| * represent runtime errors that should not be caught, as certain runtime | |
| * guarantees may not hold, making it unsafe to continue program execution. | |
| */ | |
| class Exception : Throwable | |
| { | |
| /** | |
| * Creates a new instance of Exception. The nextInChain parameter is used | |
| * internally and should always be $(D null) when passed by user code. | |
| * This constructor does not automatically throw the newly-created | |
| * Exception; the $(D throw) expression should be used for that purpose. | |
| */ | |
| @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) | |
| { | |
| super(msg, file, line, nextInChain); | |
| } | |
| @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) | |
| { | |
| super(msg, file, line, nextInChain); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| bool gotCaught; | |
| try | |
| { | |
| throw new Exception("msg"); | |
| } | |
| catch (Exception e) | |
| { | |
| gotCaught = true; | |
| assert(e.msg == "msg"); | |
| } | |
| assert(gotCaught); | |
| } | |
| @system unittest | |
| { | |
| { | |
| auto e = new Exception("msg"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__ - 2); | |
| assert(e._nextInChainPtr is null); | |
| assert(e.msg == "msg"); | |
| } | |
| { | |
| auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42); | |
| assert(e.file == "hello"); | |
| assert(e.line == 42); | |
| assert(e._nextInChainPtr !is null); | |
| assert(e.msg == "msg"); | |
| } | |
| { | |
| auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!")); | |
| assert(e.file == "hello"); | |
| assert(e.line == 42); | |
| assert(e._nextInChainPtr !is null); | |
| assert(e.msg == "msg"); | |
| } | |
| { | |
| auto e = new Exception("message"); | |
| assert(e.message == "message"); | |
| } | |
| } | |
| /** | |
| * The base class of all unrecoverable runtime errors. | |
| * | |
| * This represents the category of $(D Throwable) objects that are $(B not) | |
| * safe to catch and handle. In principle, one should not catch Error | |
| * objects, as they represent unrecoverable runtime errors. | |
| * Certain runtime guarantees may fail to hold when these errors are | |
| * thrown, making it unsafe to continue execution after catching them. | |
| */ | |
| class Error : Throwable | |
| { | |
| /** | |
| * Creates a new instance of Error. The nextInChain parameter is used | |
| * internally and should always be $(D null) when passed by user code. | |
| * This constructor does not automatically throw the newly-created | |
| * Error; the $(D throw) statement should be used for that purpose. | |
| */ | |
| @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null) | |
| { | |
| super(msg, nextInChain); | |
| bypassedException = null; | |
| } | |
| @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null) | |
| { | |
| super(msg, file, line, nextInChain); | |
| bypassedException = null; | |
| } | |
| /** The first $(D Exception) which was bypassed when this Error was thrown, | |
| or $(D null) if no $(D Exception)s were pending. */ | |
| Throwable bypassedException; | |
| } | |
| /// | |
| @system unittest | |
| { | |
| bool gotCaught; | |
| try | |
| { | |
| throw new Error("msg"); | |
| } | |
| catch (Error e) | |
| { | |
| gotCaught = true; | |
| assert(e.msg == "msg"); | |
| } | |
| assert(gotCaught); | |
| } | |
| @safe unittest | |
| { | |
| { | |
| auto e = new Error("msg"); | |
| assert(e.file is null); | |
| assert(e.line == 0); | |
| assert(e._nextInChainPtr is null); | |
| assert(e.msg == "msg"); | |
| assert(e.bypassedException is null); | |
| } | |
| { | |
| auto e = new Error("msg", new Exception("It's an Exception!")); | |
| assert(e.file is null); | |
| assert(e.line == 0); | |
| assert(e._nextInChainPtr !is null); | |
| assert(e.msg == "msg"); | |
| assert(e.bypassedException is null); | |
| } | |
| { | |
| auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!")); | |
| assert(e.file == "hello"); | |
| assert(e.line == 42); | |
| assert(e._nextInChainPtr !is null); | |
| assert(e.msg == "msg"); | |
| assert(e.bypassedException is null); | |
| } | |
| } | |
| public import core.internal.newaa : _d_aaIn, _d_aaDel, _d_aaNew, _d_aaEqual, _d_assocarrayliteralTX; | |
| public import core.internal.newaa : _d_aaLen, _d_aaGetY, _d_aaGetRvalueX, _d_aaApply, _d_aaApply2; | |
| // public import core.exception : onRangeError; // causes extra messages with -transition=fields | |
| extern (C) noreturn _d_arraybounds(string file, uint line) @trusted pure nothrow @nogc; | |
| private import core.internal.newaa; | |
| void* aaLiteral(Key, Value)(Key[] keys, Value[] values) | |
| { | |
| return _d_assocarrayliteralTX(keys, values); | |
| } | |
| // Lower an Associative Array to a newaa struct for static initialization. | |
| auto _aaAsStruct(K, V)(V[K] aa) @safe | |
| { | |
| assert(__ctfe); | |
| return makeAA!(K, V)(aa); | |
| } | |
| alias AssociativeArray(Key, Value) = Value[Key]; | |
| /*********************************** | |
| * Removes all remaining keys and values from an associative array. | |
| * Params: | |
| * aa = The associative array. | |
| */ | |
| void clear(Value, Key)(Value[Key] aa) @trusted | |
| if (!is(Value == shared)) | |
| { | |
| _aaClear(aa); | |
| } | |
| /** ditto */ | |
| void clear(Value, Key)(Value[Key]* aa) @trusted | |
| if (!is(Value == shared)) | |
| { | |
| (*aa).clear(); | |
| } | |
| /** ditto */ | |
| void clear(Value, Key)(shared(Value)[Key] aa) | |
| { | |
| return (cast(Value[Key])aa).clear(); | |
| } | |
| /** ditto */ | |
| void clear(Value, Key)(shared(Value)[Key]* aa) | |
| { | |
| (cast(Value[Key])*aa).clear(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto aa = ["k1": 2]; | |
| aa.clear; | |
| assert("k1" !in aa); | |
| } | |
| // Issue 20559 | |
| @system unittest | |
| { | |
| static class Foo | |
| { | |
| int[string] aa; | |
| alias aa this; | |
| } | |
| auto v = new Foo(); | |
| v["Hello World"] = 42; | |
| v.clear; | |
| assert("Hello World" !in v); | |
| // Test for T* | |
| static assert(!__traits(compiles, (&v).clear)); | |
| static assert( __traits(compiles, (*(&v)).clear)); | |
| } | |
| /*********************************** | |
| * Reorganizes the associative array in place so that lookups are more | |
| * efficient. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * The rehashed associative array. | |
| * Note: | |
| * emulated by the compiler during CTFE | |
| */ | |
| Value[Key] rehash(Value, Key)(Value[Key] aa) | |
| { | |
| return _aaRehash(aa); | |
| } | |
| /** ditto */ | |
| Value[Key] rehash(T : Value[Key], Value, Key)(T* aa) | |
| { | |
| return (*aa).rehash(); // CTFE only intercepts the non-pointer overload | |
| } | |
| /** ditto */ | |
| Value[Key] rehash(T : shared Value[Key], Value, Key)(auto ref T aa) | |
| { | |
| return (cast(Value[Key])aa).rehash(); // CTFE only intercepts the V[K] overload | |
| } | |
| /** ditto */ | |
| Value[Key] rehash(T : shared Value[Key], Value, Key)(T* aa) | |
| { | |
| return (cast(Value[Key])*aa).rehash(); // CTFE only intercepts the non-pointer overload | |
| } | |
| /*********************************** | |
| * Creates a new associative array of the same size and copies the contents of | |
| * the associative array into it. | |
| * Params: | |
| * aa = The associative array. | |
| * Note: | |
| * emulated by the compiler during CTFE | |
| */ | |
| auto dup(T : V[K], K, V)(T aa) | |
| { | |
| import core.internal.traits : substInout, Unconst; | |
| // Bug10720 - check whether V is copyable | |
| static if (is(typeof({ Unconst!V v = aa[K.init]; }))) | |
| alias Vret = Unconst!V; | |
| else static if (is(typeof({ V v = aa[K.init]; }))) | |
| alias Vret = V; | |
| else | |
| static assert(false, "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable"); | |
| alias Kret = typeof([K.init][0]); // strip const if possible by copy | |
| alias K1 = substInout!K; | |
| alias V1 = substInout!Vret; | |
| auto naa = _aaDup((() @trusted => cast(V1[K1])aa)()); | |
| auto maa = ((inout T) @trusted => cast(Vret[Kret])naa)(aa); | |
| return maa; | |
| } | |
| /** ditto */ | |
| auto dup(T : V[K], K, V)(T* aa) | |
| { | |
| return (*aa).dup; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto aa = ["k1": 2]; | |
| auto a2 = aa.dup; | |
| aa["k2"] = 3; | |
| assert("k2" !in a2); | |
| } | |
| // this should never be made public. | |
| private auto _aaToRange(K, V)(auto ref inout V[K] aa) @trusted | |
| { | |
| import core.internal.traits : substInout; | |
| alias K2 = substInout!K; | |
| alias V2 = substInout!V; | |
| auto aa2 = cast(V2[K2])aa; | |
| return _aaRange(aa2); | |
| } | |
| /*********************************** | |
| * Returns a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | |
| * which will iterate over the keys of the associative array. The keys are | |
| * returned by reference. | |
| * | |
| * If structural changes are made to the array (removing or adding keys), all | |
| * ranges previously obtained through this function are invalidated. The | |
| * following example program will dereference a null pointer: | |
| * | |
| *--- | |
| * import std.stdio : writeln; | |
| * | |
| * auto dict = ["k1": 1, "k2": 2]; | |
| * auto keyRange = dict.byKey; | |
| * dict.clear; | |
| * writeln(keyRange.front); // Segmentation fault | |
| *--- | |
| * | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A forward range referencing the keys of the associative array. | |
| */ | |
| auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe | |
| { | |
| import core.internal.traits : substInout; | |
| const(V[K]) aa2 = aa; | |
| static struct Result | |
| { | |
| typeof(_aaToRange(aa2)) r; | |
| pure nothrow @nogc: | |
| @property bool empty() @safe { return _aaRangeEmpty(r); } | |
| @property ref front() @trusted | |
| { | |
| return *cast(substInout!K*)_aaRangeFrontKey(r); | |
| } | |
| void popFront() @safe { _aaRangePopFront(r); } | |
| @property Result save() { return this; } | |
| } | |
| return Result(_aaToRange(aa2)); | |
| } | |
| /** ditto */ | |
| auto byKey(K, V)(V[K]* aa) pure nothrow @nogc | |
| { | |
| return (*aa).byKey(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto dict = [1: "v1", 2: "v2"]; | |
| int sum; | |
| foreach (v; dict.byKey) | |
| sum += v; | |
| assert(sum == 3); | |
| } | |
| /*********************************** | |
| * Returns a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | |
| * which will iterate over the values of the associative array. The values are | |
| * returned by reference. | |
| * | |
| * If structural changes are made to the array (removing or adding keys), all | |
| * ranges previously obtained through this function are invalidated. The | |
| * following example program will dereference a null pointer: | |
| * | |
| *--- | |
| * import std.stdio : writeln; | |
| * | |
| * auto dict = ["k1": 1, "k2": 2]; | |
| * auto valueRange = dict.byValue; | |
| * dict.clear; | |
| * writeln(valueRange.front); // Segmentation fault | |
| *--- | |
| * | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A forward range referencing the values of the associative array. | |
| */ | |
| auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe | |
| { | |
| import core.internal.traits : substInout; | |
| const(V[K]) aa2 = aa; | |
| static struct Result | |
| { | |
| typeof(_aaToRange(aa2)) r; | |
| pure nothrow @nogc: | |
| @property bool empty() @safe { return _aaRangeEmpty(r); } | |
| @property ref front() @trusted | |
| { | |
| return *cast(substInout!V*)_aaRangeFrontValue(r); | |
| } | |
| void popFront() @safe { _aaRangePopFront(r); } | |
| @property Result save() { return this; } | |
| } | |
| return Result(_aaToRange(aa2)); | |
| } | |
| /** ditto */ | |
| auto byValue(K, V)(V[K]* aa) pure nothrow @nogc | |
| { | |
| return (*aa).byValue(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto dict = ["k1": 1, "k2": 2]; | |
| int sum; | |
| foreach (v; dict.byValue) | |
| sum += v; | |
| assert(sum == 3); | |
| foreach (ref v; dict.byValue) | |
| v++; | |
| assert(dict == ["k1": 2, "k2": 3]); | |
| } | |
| /*********************************** | |
| * Returns a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | |
| * which will iterate over the key-value pairs of the associative array. The | |
| * returned pairs are represented by an opaque type with `.key` and `.value` | |
| * properties for accessing references to the key and value of the pair, | |
| * respectively. | |
| * | |
| * If structural changes are made to the array (removing or adding keys), all | |
| * ranges previously obtained through this function are invalidated. The | |
| * following example program will dereference a null pointer: | |
| * | |
| *--- | |
| * import std.stdio : writeln; | |
| * | |
| * auto dict = ["k1": 1, "k2": 2]; | |
| * auto kvRange = dict.byKeyValue; | |
| * dict.clear; | |
| * writeln(kvRange.front.key, ": ", kvRange.front.value); // Segmentation fault | |
| *--- | |
| * | |
| * Note that this is a low-level interface to iterating over the associative | |
| * array and is not compatible with the | |
| * $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) type in Phobos. | |
| * For compatibility with `Tuple`, use | |
| * $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead. | |
| * | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A forward range referencing the pairs of the associative array. | |
| */ | |
| auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe | |
| { | |
| import core.internal.traits : substInout; | |
| const(V[K]) aa2 = aa; | |
| static struct Result | |
| { | |
| typeof(_aaToRange(aa2)) r; | |
| pure nothrow @nogc: | |
| @property bool empty() @safe { return _aaRangeEmpty(r); } | |
| @property auto front() | |
| { | |
| static struct Pair | |
| { | |
| // We save the pointers here so that the Pair we return | |
| // won't mutate when Result.popFront is called afterwards. | |
| private const(substInout!K)* keyp; | |
| private const(substInout!V)* valp; | |
| @property ref key() inout @trusted | |
| { | |
| return *cast(K*)keyp; | |
| } | |
| @property ref value() inout @trusted | |
| { | |
| return *cast(V*)valp; | |
| } | |
| } | |
| return Pair(_aaRangeFrontKey(r), | |
| _aaRangeFrontValue(r)); | |
| } | |
| void popFront() @safe { return _aaRangePopFront(r); } | |
| @property Result save() { return this; } | |
| } | |
| return Result(_aaToRange(aa2)); | |
| } | |
| /** ditto */ | |
| auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc | |
| { | |
| return (*aa).byKeyValue(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto dict = ["k1": 1, "k2": 2]; | |
| int sum; | |
| foreach (e; dict.byKeyValue) | |
| { | |
| assert(e.key[1] == e.value + '0'); | |
| sum += e.value; | |
| } | |
| assert(sum == 3); | |
| foreach (e; dict.byKeyValue) | |
| e.value++; | |
| assert(dict == ["k1": 2, "k2": 3]); | |
| } | |
| /*********************************** | |
| * Returns a newly allocated dynamic array containing a copy of the keys from | |
| * the associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A dynamic array containing a copy of the keys. | |
| * Note: | |
| * emulated by the compiler during CTFE | |
| */ | |
| auto keys(Value, Key)(inout Value[Key] aa) @property | |
| { | |
| return _aaKeys!(Key, Value)(aa); | |
| } | |
| /** ditto */ | |
| auto keys(T : Value[Key], Value, Key)(T *aa) @property | |
| { | |
| return (*aa).keys; // CTFE only intercepts the non-pointer overload | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto aa = [1: "v1", 2: "v2"]; | |
| int sum; | |
| foreach (k; aa.keys) | |
| sum += k; | |
| assert(sum == 3); | |
| } | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| string str; | |
| void[][string] dict; | |
| alias dict this; | |
| } | |
| auto s = S("a"); | |
| assert(s.keys.length == 0); | |
| } | |
| @safe unittest | |
| { | |
| @safe static struct Key | |
| { | |
| string str; | |
| this(this) @safe {} | |
| } | |
| string[Key] aa; | |
| static assert(__traits(compiles, { | |
| void test() @safe { | |
| const _ = aa.keys; | |
| } | |
| })); | |
| } | |
| @safe unittest | |
| { | |
| static struct Key | |
| { | |
| string str; | |
| this(this) @system {} | |
| } | |
| string[Key] aa; | |
| static assert(!__traits(compiles, { | |
| void test() @safe { | |
| const _ = aa.keys; | |
| } | |
| })); | |
| } | |
| /*********************************** | |
| * Returns a newly allocated dynamic array containing a copy of the values from | |
| * the associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A dynamic array containing a copy of the values. | |
| * Note: | |
| * emulated by the compiler during CTFE | |
| */ | |
| auto values(Value, Key)(inout Value[Key] aa) @property | |
| { | |
| return _aaValues!(Key, Value)(aa); | |
| } | |
| /** ditto */ | |
| auto values(T : Value[Key], Value, Key)(T *aa) @property | |
| { | |
| return (*aa).values; // CTFE only intercepts the non-pointer overload | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto aa = ["k1": 1, "k2": 2]; | |
| int sum; | |
| foreach (e; aa.values) | |
| sum += e; | |
| assert(sum == 3); | |
| } | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| string str; | |
| void[][string] dict; | |
| alias dict this; | |
| } | |
| auto s = S("a"); | |
| assert(s.values.length == 0); | |
| } | |
| @safe unittest | |
| { | |
| @safe static struct Value | |
| { | |
| string str; | |
| this(this) @safe {} | |
| } | |
| Value[string] aa; | |
| static assert(__traits(compiles, { | |
| void test() @safe { | |
| const _ = aa.values; | |
| } | |
| })); | |
| } | |
| @safe unittest | |
| { | |
| static struct Value | |
| { | |
| string str; | |
| this(this) @system {} | |
| } | |
| Value[string] aa; | |
| static assert(!__traits(compiles, { | |
| void test() @safe { | |
| const _ = aa.values; | |
| } | |
| })); | |
| } | |
| /*********************************** | |
| * If `key` is in `aa`, returns corresponding value; otherwise it evaluates and | |
| * returns `defaultValue`. | |
| * Params: | |
| * aa = The associative array. | |
| * key = The key. | |
| * defaultValue = The default value. | |
| * Returns: | |
| * The value. | |
| */ | |
| inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) | |
| { | |
| auto p = key in aa; | |
| return p ? *p : defaultValue; | |
| } | |
| /** ditto */ | |
| inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) | |
| { | |
| return (*aa).get(key, defaultValue); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto aa = ["k1": 1]; | |
| assert(aa.get("k1", 0) == 1); | |
| assert(aa.get("k2", 0) == 0); | |
| } | |
| /*********************************** | |
| * If `key` is in `aa`, returns corresponding value; otherwise it evaluates | |
| * `value`, adds it to the associative array and returns it. | |
| * Params: | |
| * aa = The associative array. | |
| * key = The key. | |
| * value = The required value. | |
| * Returns: | |
| * The value. | |
| */ | |
| ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) | |
| { | |
| bool found; | |
| auto p = _aaGetX(aa, key, found, value); | |
| return *p; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto aa = ["k1": 1]; | |
| assert(aa.require("k1", 0) == 1); | |
| assert(aa.require("k2", 0) == 0); | |
| assert(aa["k2"] == 0); | |
| } | |
| // Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test. | |
| private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); })); | |
| /*********************************** | |
| * Calls `create` if `key` doesn't exist in the associative array, | |
| * otherwise calls `update`. | |
| * `create` returns a corresponding value for `key`. | |
| * `update` accepts a value parameter. If it returns a value, the value is | |
| * set for `key`. | |
| * Params: | |
| * aa = The associative array. | |
| * key = The key. | |
| * create = The callable to create a value for `key`. | |
| * Must return V. | |
| * update = The callable to call if `key` exists. | |
| * Takes a V argument, returns a V or void. | |
| */ | |
| void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) | |
| if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void))) | |
| { | |
| bool found; | |
| auto p = _aaGetX(aa, key, found, create()); | |
| if (found) | |
| { | |
| static if (is(typeof(update(*p)) == void)) | |
| update(*p); | |
| else | |
| *p = update(*p); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int[string] aa; | |
| // create | |
| aa.update("key", | |
| () => 1, | |
| (int) {} // not executed | |
| ); | |
| assert(aa["key"] == 1); | |
| // update value by ref | |
| aa.update("key", | |
| () => 0, // not executed | |
| (ref int v) { | |
| v += 1; | |
| }); | |
| assert(aa["key"] == 2); | |
| // update from return value | |
| aa.update("key", | |
| () => 0, // not executed | |
| (int v) => v * 2 | |
| ); | |
| assert(aa["key"] == 4); | |
| // 'update' without changing value | |
| aa.update("key", | |
| () => 0, // not executed | |
| (int) { | |
| // do something else | |
| }); | |
| assert(aa["key"] == 4); | |
| } | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| int x; | |
| @nogc nothrow pure: | |
| this(this) @system {} | |
| @safe const: | |
| // stubs | |
| bool opEquals(S rhs) { assert(0); } | |
| size_t toHash() const { assert(0); } | |
| } | |
| int[string] aai; | |
| static assert(is(typeof(() @safe { aai.require("a", 1234); }))); | |
| static assert(is(typeof(() @safe { aai.update("a", { return 1234; }, (ref int x) { x++; return x; }); }))); | |
| S[string] aas; | |
| static assert(is(typeof(() { aas.require("a", S(1234)); }))); | |
| static assert(is(typeof(() { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); }))); | |
| static assert(!is(typeof(() @safe { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); }))); | |
| int[S] aais; | |
| static assert(is(typeof(() { aais.require(S(1234), 1234); }))); | |
| static assert(is(typeof(() { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); }))); | |
| static assert(!is(typeof(() @safe { aais.require(S(1234), 1234); }))); | |
| static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); }))); | |
| } | |
| @safe unittest | |
| { | |
| struct S0 | |
| { | |
| int opCall(ref int v) | |
| { | |
| return v + 1; | |
| } | |
| } | |
| struct S1 | |
| { | |
| int opCall()() | |
| { | |
| return -2; | |
| } | |
| T opCall(T)(ref T v) | |
| { | |
| return v + 1; | |
| } | |
| } | |
| int[string] a = ["2" : 1]; | |
| a.update("2", () => -1, S0.init); | |
| assert(a["2"] == 2); | |
| a.update("0", () => -1, S0.init); | |
| assert(a["0"] == -1); | |
| a.update("2", S1.init, S1.init); | |
| assert(a["2"] == 3); | |
| a.update("1", S1.init, S1.init); | |
| assert(a["1"] == -2); | |
| } | |
| @system unittest | |
| { | |
| int[string] aa; | |
| foreach (n; 0 .. 2) | |
| aa.update("k1", { | |
| return 7; | |
| }, (ref int v) { | |
| return v + 3; | |
| }); | |
| assert(aa["k1"] == 10); | |
| } | |
| version (CoreDdoc) | |
| { | |
| // This lets DDoc produce better documentation. | |
| /** | |
| Calculates the hash value of `arg` with an optional `seed` initial value. | |
| The result might not be equal to `typeid(T).getHash(&arg)`. | |
| Params: | |
| arg = argument to calculate the hash value of | |
| seed = optional `seed` value (may be used for hash chaining) | |
| Return: calculated hash value of `arg` | |
| */ | |
| size_t hashOf(T)(auto ref T arg, size_t seed) | |
| { | |
| static import core.internal.hash; | |
| return core.internal.hash.hashOf(arg, seed); | |
| } | |
| /// ditto | |
| size_t hashOf(T)(auto ref T arg) | |
| { | |
| static import core.internal.hash; | |
| return core.internal.hash.hashOf(arg); | |
| } | |
| @safe unittest | |
| { | |
| auto h1 = "my.string".hashOf; | |
| assert(h1 == "my.string".hashOf); | |
| } | |
| } | |
| else | |
| { | |
| public import core.internal.hash : hashOf; | |
| } | |
| /// | |
| @system unittest | |
| { | |
| class MyObject | |
| { | |
| size_t myMegaHash() const @safe pure nothrow | |
| { | |
| return 42; | |
| } | |
| } | |
| struct Test | |
| { | |
| int a; | |
| string b; | |
| MyObject c; | |
| size_t toHash() const pure nothrow | |
| { | |
| size_t hash = a.hashOf(); | |
| hash = b.hashOf(hash); | |
| size_t h1 = c.myMegaHash(); | |
| hash = h1.hashOf(hash); //Mix two hash values | |
| return hash; | |
| } | |
| } | |
| } | |
| bool _xopEquals(in void*, in void*) | |
| { | |
| throw new Error("TypeInfo.equals is not implemented"); | |
| } | |
| bool _xopCmp(in void*, in void*) | |
| { | |
| throw new Error("TypeInfo.compare is not implemented"); | |
| } | |
| /****************************************** | |
| * Create RTInfo for type T | |
| */ | |
| template RTInfoImpl(size_t[] pointerBitmap) | |
| { | |
| immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[]; | |
| } | |
| template RTInfo(T) | |
| { | |
| enum pointerBitmap = __traits(getPointerBitmap, T); | |
| static if (pointerBitmap[1 .. $] == size_t[pointerBitmap.length - 1].init) | |
| enum RTInfo = rtinfoNoPointers; | |
| else | |
| enum RTInfo = RTInfoImpl!(pointerBitmap).ptr; | |
| } | |
| /** | |
| * shortcuts for the precise GC, also generated by the compiler | |
| * used instead of the actual pointer bitmap | |
| */ | |
| enum immutable(void)* rtinfoNoPointers = null; | |
| enum immutable(void)* rtinfoHasPointers = cast(void*)1; | |
| // Helper functions | |
| private inout(TypeInfo) getElement(return scope inout TypeInfo value) @trusted pure nothrow | |
| { | |
| TypeInfo element = cast() value; | |
| for (;;) | |
| { | |
| if (auto qualified = cast(TypeInfo_Const) element) | |
| element = qualified.base; | |
| else if (auto redefined = cast(TypeInfo_Enum) element) | |
| element = redefined.base; | |
| else if (auto staticArray = cast(TypeInfo_StaticArray) element) | |
| element = staticArray.value; | |
| else if (auto vector = cast(TypeInfo_Vector) element) | |
| element = vector.base; | |
| else | |
| break; | |
| } | |
| return cast(inout) element; | |
| } | |
| private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, const size_t count) @trusted nothrow | |
| { | |
| if (!count) | |
| return 0; | |
| const size_t elementSize = element.tsize; | |
| if (!elementSize) | |
| return 0; | |
| static bool hasCustomToHash(const scope TypeInfo value) @trusted pure nothrow | |
| { | |
| const element = getElement(value); | |
| if (const struct_ = cast(const TypeInfo_Struct) element) | |
| return !!struct_.xtoHash; | |
| return cast(const TypeInfo_Array) element | |
| || cast(const TypeInfo_AssociativeArray) element | |
| || cast(const ClassInfo) element | |
| || cast(const TypeInfo_Interface) element; | |
| } | |
| if (!hasCustomToHash(element)) | |
| return hashOf(ptr[0 .. elementSize * count]); | |
| size_t hash = 0; | |
| foreach (size_t i; 0 .. count) | |
| hash = hashOf(element.getHash(ptr + i * elementSize), hash); | |
| return hash; | |
| } | |
| /// Provide the .dup array property, which creates a duplicate. | |
| @property auto dup(T)(T[] a) | |
| if (!is(const(T) : T)) | |
| { | |
| import core.internal.traits : Unconst; | |
| import core.internal.array.duplication : _dup; | |
| static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~ | |
| " to "~Unconst!T.stringof~" in dup."); | |
| return _dup!(T, Unconst!T)(a); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| auto arr = [1, 2]; | |
| auto arr2 = arr.dup; | |
| arr[0] = 0; | |
| assert(arr == [0, 2]); | |
| assert(arr2 == [1, 2]); | |
| } | |
| /// ditto | |
| // const overload to support implicit conversion to immutable (unique result, see DIP29) | |
| @property T[] dup(T)(const(T)[] a) | |
| if (is(const(T) : T)) | |
| { | |
| import core.internal.array.duplication : _dup; | |
| return _dup!(const(T), T)(a); | |
| } | |
| /// Provide the .idup array property, which creates an immutable duplicate. | |
| @property immutable(T)[] idup(T)(T[] a) | |
| { | |
| import core.internal.array.duplication : _dup; | |
| static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~ | |
| " to immutable in idup."); | |
| return _dup!(T, immutable(T))(a); | |
| } | |
| /// ditto | |
| @property immutable(T)[] idup(T:void)(const(T)[] a) | |
| { | |
| return a.dup; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| char[] arr = ['a', 'b', 'c']; | |
| string s = arr.idup; | |
| arr[0] = '.'; | |
| assert(s == "abc"); | |
| } | |
| // HACK: This is a lie. `_d_arraysetcapacity` is neither `nothrow` nor `pure`, but this lie is | |
| // necessary for now to prevent breaking code. | |
| import core.internal.array.capacity : _d_arraysetcapacityPureNothrow; | |
| import core.internal.traits: Unqual; | |
| /** | |
| (Property) Gets the current _capacity of a slice. The _capacity is the size | |
| that the slice can grow to before the underlying array must be | |
| reallocated or extended. | |
| If an append must reallocate a slice with no possibility of extension, then | |
| `0` is returned. This happens when the slice references a static array, or | |
| if another slice references elements past the end of the current slice. | |
| Note: The _capacity of a slice may be impacted by operations on other slices. | |
| */ | |
| @property size_t capacity(T)(T[] arr) pure nothrow @trusted | |
| { | |
| const isshared = is(T == shared); | |
| alias Unqual_T = Unqual!T; | |
| // The postblit of T may be impure, so we need to use the `pure nothrow` wrapper | |
| return _d_arraysetcapacityPureNothrow!Unqual_T(0, cast(void[]*)&arr, isshared); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| //Static array slice: no capacity | |
| int[4] sarray = [1, 2, 3, 4]; | |
| int[] slice = sarray[]; | |
| assert(sarray.capacity == 0); | |
| //Appending to slice will reallocate to a new array | |
| slice ~= 5; | |
| assert(slice.capacity >= 5); | |
| //Dynamic array slices | |
| int[] a = [1, 2, 3, 4]; | |
| int[] b = a[1 .. $]; | |
| int[] c = a[1 .. $ - 1]; | |
| debug(SENTINEL) {} else // non-zero capacity very much depends on the array and GC implementation | |
| { | |
| assert(a.capacity != 0); | |
| assert(a.capacity == b.capacity + 1); //both a and b share the same tail | |
| } | |
| assert(c.capacity == 0); //an append to c must relocate c. | |
| } | |
| /** | |
| Reserves capacity for a slice. The capacity is the size | |
| that the slice can grow to before the underlying array must be | |
| reallocated or extended. | |
| Returns: The new capacity of the array (which may be larger than | |
| the requested capacity). | |
| */ | |
| size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted | |
| { | |
| if (__ctfe) | |
| return newcapacity; | |
| else | |
| { | |
| const isshared = is(T == shared); | |
| alias Unqual_T = Unqual!T; | |
| // The postblit of T may be impure, so we need to use the `pure nothrow` wrapper | |
| return _d_arraysetcapacityPureNothrow!Unqual_T(newcapacity, cast(void[]*)&arr, isshared); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| //Static array slice: no capacity. Reserve relocates. | |
| int[4] sarray = [1, 2, 3, 4]; | |
| int[] slice = sarray[]; | |
| auto u = slice.reserve(8); | |
| assert(u >= 8); | |
| assert(&sarray[0] !is &slice[0]); | |
| assert(slice.capacity == u); | |
| //Dynamic array slices | |
| int[] a = [1, 2, 3, 4]; | |
| a.reserve(8); //prepare a for appending 4 more items | |
| auto p = &a[0]; | |
| u = a.capacity; | |
| a ~= [5, 6, 7, 8]; | |
| assert(p == &a[0]); //a should not have been reallocated | |
| assert(u == a.capacity); //a should not have been extended | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=12330, reserve() at CTFE time | |
| @safe unittest | |
| { | |
| int[] foo() { | |
| int[] result; | |
| auto a = result.reserve = 5; | |
| assert(a == 5); | |
| return result; | |
| } | |
| enum r = foo(); | |
| } | |
| // Issue 6646: should be possible to use array.reserve from SafeD. | |
| @safe unittest | |
| { | |
| int[] a; | |
| a.reserve(10); | |
| } | |
| /** | |
| Assume that it is safe to append to this array. Appends made to this array | |
| after calling this function may append in place, even if the array was a | |
| slice of a larger array to begin with. | |
| Use this only when it is certain there are no elements in use beyond the | |
| array in the memory block. If there are, those elements will be | |
| overwritten by appending to this array. | |
| Warning: Calling this function, and then using references to data located after the | |
| given array results in undefined behavior. | |
| Returns: | |
| The input is returned. | |
| */ | |
| auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system | |
| { | |
| import core.internal.array.capacity : _d_arrayshrinkfit; | |
| import core.internal.traits : Unqual; | |
| alias Unqual_Tarr = Unqual!T[]; | |
| enum isshared = is(T == shared); | |
| _d_arrayshrinkfit(cast(Unqual_Tarr)arr, isshared); | |
| return arr; | |
| } | |
| /// | |
| @system unittest | |
| { | |
| int[] a = [1, 2, 3, 4]; | |
| // Without assumeSafeAppend. Appending relocates. | |
| int[] b = a [0 .. 3]; | |
| b ~= 5; | |
| assert(a.ptr != b.ptr); | |
| debug(SENTINEL) {} else | |
| { | |
| // With assumeSafeAppend. Appending overwrites. | |
| int[] c = a [0 .. 3]; | |
| c.assumeSafeAppend() ~= 5; | |
| assert(a.ptr == c.ptr); | |
| } | |
| } | |
| @system unittest | |
| { | |
| int[] arr; | |
| auto newcap = arr.reserve(2000); | |
| assert(newcap >= 2000); | |
| assert(newcap == arr.capacity); | |
| auto ptr = arr.ptr; | |
| foreach (i; 0..2000) | |
| arr ~= i; | |
| assert(ptr == arr.ptr); | |
| arr = arr[0..1]; | |
| arr.assumeSafeAppend(); | |
| arr ~= 5; | |
| assert(ptr == arr.ptr); | |
| } | |
| @system unittest | |
| { | |
| int[] arr = [1, 2, 3]; | |
| void foo(ref int[] i) | |
| { | |
| i ~= 5; | |
| } | |
| arr = arr[0 .. 2]; | |
| foo(assumeSafeAppend(arr)); //pass by ref | |
| assert(arr[]==[1, 2, 5]); | |
| arr = arr[0 .. 1].assumeSafeAppend(); //pass by value | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=10574 | |
| @system unittest | |
| { | |
| int[] a; | |
| immutable(int[]) b; | |
| auto a2 = &assumeSafeAppend(a); | |
| auto b2 = &assumeSafeAppend(b); | |
| auto a3 = assumeSafeAppend(a[]); | |
| auto b3 = assumeSafeAppend(b[]); | |
| assert(is(typeof(*a2) == int[])); | |
| assert(is(typeof(*b2) == immutable(int[]))); | |
| assert(is(typeof(a3) == int[])); | |
| assert(is(typeof(b3) == immutable(int[]))); | |
| } | |
| private void _doPostblit(T)(T[] arr) | |
| { | |
| // infer static postblit type, run postblit if any | |
| static if (__traits(hasPostblit, T)) | |
| { | |
| static if (__traits(isStaticArray, T) && is(T : E[], E)) | |
| _doPostblit(cast(E[]) arr); | |
| else static if (!is(typeof(arr[0].__xpostblit())) && is(immutable T == immutable U, U)) | |
| foreach (ref elem; (() @trusted => cast(U[]) arr)()) | |
| elem.__xpostblit(); | |
| else | |
| foreach (ref elem; arr) | |
| elem.__xpostblit(); | |
| } | |
| } | |
| /** | |
| Destroys the given object and optionally resets to initial state. It's used to | |
| _destroy an object, calling its destructor or finalizer so it no longer | |
| references any other objects. It does $(I not) initiate a GC cycle or free | |
| any GC memory. | |
| If `initialize` is supplied `false`, the object is considered invalid after | |
| destruction, and should not be referenced. | |
| */ | |
| void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct)) | |
| { | |
| import core.internal.destruction : destructRecurse; | |
| destructRecurse(obj); | |
| static if (initialize) | |
| { | |
| import core.internal.lifetime : emplaceInitializer; | |
| emplaceInitializer(obj); // emplace T.init | |
| } | |
| } | |
| @safe unittest | |
| { | |
| struct A { string s = "A"; } | |
| A a = {s: "B"}; | |
| assert(a.s == "B"); | |
| a.destroy; | |
| assert(a.s == "A"); | |
| } | |
| nothrow @safe @nogc unittest | |
| { | |
| { | |
| struct A { string s = "A"; } | |
| A a; | |
| a.s = "asd"; | |
| destroy!false(a); | |
| assert(a.s == "asd"); | |
| destroy(a); | |
| assert(a.s == "A"); | |
| } | |
| { | |
| static int destroyed = 0; | |
| struct C | |
| { | |
| string s = "C"; | |
| ~this() nothrow @safe @nogc | |
| { | |
| destroyed ++; | |
| } | |
| } | |
| struct B | |
| { | |
| C c; | |
| string s = "B"; | |
| ~this() nothrow @safe @nogc | |
| { | |
| destroyed ++; | |
| } | |
| } | |
| B a; | |
| a.s = "asd"; | |
| a.c.s = "jkl"; | |
| destroy!false(a); | |
| assert(destroyed == 2); | |
| assert(a.s == "asd"); | |
| assert(a.c.s == "jkl" ); | |
| destroy(a); | |
| assert(destroyed == 4); | |
| assert(a.s == "B"); | |
| assert(a.c.s == "C" ); | |
| } | |
| } | |
| private extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow; | |
| /// ditto | |
| void destroy(bool initialize = true, T)(T obj) if (is(T == class)) | |
| { | |
| static if (__traits(getLinkage, T) == "C++") | |
| { | |
| static if (__traits(hasMember, T, "__xdtor")) | |
| obj.__xdtor(); | |
| static if (initialize) | |
| { | |
| const initializer = __traits(initSymbol, T); | |
| (cast(void*)obj)[0 .. initializer.length] = initializer[]; | |
| } | |
| } | |
| else | |
| { | |
| // Bypass overloaded opCast | |
| auto ptr = (() @trusted => *cast(void**) &obj)(); | |
| rt_finalize2(ptr, true, initialize); | |
| } | |
| } | |
| /// ditto | |
| void destroy(bool initialize = true, T)(T obj) if (is(T == interface)) | |
| { | |
| static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface"); | |
| destroy!initialize(cast(Object)obj); | |
| } | |
| /// Reference type demonstration | |
| @system unittest | |
| { | |
| class C | |
| { | |
| struct Agg | |
| { | |
| static int dtorCount; | |
| int x = 10; | |
| ~this() { dtorCount++; } | |
| } | |
| static int dtorCount; | |
| string s = "S"; | |
| Agg a; | |
| ~this() { dtorCount++; } | |
| } | |
| C c = new C(); | |
| assert(c.dtorCount == 0); // destructor not yet called | |
| assert(c.s == "S"); // initial state `c.s` is `"S"` | |
| assert(c.a.dtorCount == 0); // destructor not yet called | |
| assert(c.a.x == 10); // initial state `c.a.x` is `10` | |
| c.s = "T"; | |
| c.a.x = 30; | |
| assert(c.s == "T"); // `c.s` is `"T"` | |
| destroy(c); | |
| assert(c.dtorCount == 1); // `c`'s destructor was called | |
| assert(c.s == "S"); // `c.s` is back to its inital state, `"S"` | |
| assert(c.a.dtorCount == 1); // `c.a`'s destructor was called | |
| assert(c.a.x == 10); // `c.a.x` is back to its inital state, `10` | |
| } | |
| /// C++ classes work too | |
| @system unittest | |
| { | |
| extern (C++) class CPP | |
| { | |
| struct Agg | |
| { | |
| __gshared int dtorCount; | |
| int x = 10; | |
| ~this() { dtorCount++; } | |
| } | |
| __gshared int dtorCount; | |
| string s = "S"; | |
| Agg a; | |
| ~this() { dtorCount++; } | |
| } | |
| CPP cpp = new CPP(); | |
| assert(cpp.dtorCount == 0); // destructor not yet called | |
| assert(cpp.s == "S"); // initial state `cpp.s` is `"S"` | |
| assert(cpp.a.dtorCount == 0); // destructor not yet called | |
| assert(cpp.a.x == 10); // initial state `cpp.a.x` is `10` | |
| cpp.s = "T"; | |
| cpp.a.x = 30; | |
| assert(cpp.s == "T"); // `cpp.s` is `"T"` | |
| destroy!false(cpp); // destroy without initialization | |
| assert(cpp.dtorCount == 1); // `cpp`'s destructor was called | |
| assert(cpp.s == "T"); // `cpp.s` is not initialized | |
| assert(cpp.a.dtorCount == 1); // `cpp.a`'s destructor was called | |
| assert(cpp.a.x == 30); // `cpp.a.x` is not initialized | |
| destroy(cpp); | |
| assert(cpp.dtorCount == 2); // `cpp`'s destructor was called again | |
| assert(cpp.s == "S"); // `cpp.s` is back to its inital state, `"S"` | |
| assert(cpp.a.dtorCount == 2); // `cpp.a`'s destructor was called again | |
| assert(cpp.a.x == 10); // `cpp.a.x` is back to its inital state, `10` | |
| } | |
| /// Value type demonstration | |
| @safe unittest | |
| { | |
| int i; | |
| assert(i == 0); // `i`'s initial state is `0` | |
| i = 1; | |
| assert(i == 1); // `i` changed to `1` | |
| destroy!false(i); | |
| assert(i == 1); // `i` was not initialized | |
| destroy(i); | |
| assert(i == 0); // `i` is back to its initial state `0` | |
| } | |
| /// Nested struct type | |
| @system unittest | |
| { | |
| int dtorCount; | |
| struct A | |
| { | |
| int i; | |
| ~this() | |
| { | |
| dtorCount++; // capture local variable | |
| } | |
| } | |
| A a = A(5); | |
| destroy!false(a); | |
| assert(dtorCount == 1); | |
| assert(a.i == 5); | |
| destroy(a); | |
| assert(dtorCount == 2); | |
| assert(a.i == 0); | |
| // the context pointer is now null | |
| // restore it so the dtor can run | |
| import core.lifetime : emplace; | |
| emplace(&a, A(0)); | |
| // dtor also called here | |
| } | |
| @system unittest | |
| { | |
| extern(C++) | |
| static class C | |
| { | |
| void* ptr; | |
| this() {} | |
| } | |
| destroy!false(new C()); | |
| destroy!true(new C()); | |
| } | |
| @system unittest | |
| { | |
| // class with an `alias this` | |
| class A | |
| { | |
| static int dtorCount; | |
| ~this() | |
| { | |
| dtorCount++; | |
| } | |
| } | |
| class B | |
| { | |
| A a; | |
| alias a this; | |
| this() | |
| { | |
| a = new A; | |
| } | |
| static int dtorCount; | |
| ~this() | |
| { | |
| dtorCount++; | |
| } | |
| } | |
| auto b = new B; | |
| assert(A.dtorCount == 0); | |
| assert(B.dtorCount == 0); | |
| destroy(b); | |
| assert(A.dtorCount == 0); | |
| assert(B.dtorCount == 1); | |
| auto a = new A; | |
| destroy(a); | |
| assert(A.dtorCount == 1); | |
| } | |
| @system unittest | |
| { | |
| interface I { } | |
| { | |
| class A: I { string s = "A"; this() {} } | |
| auto a = new A, b = new A; | |
| a.s = b.s = "asd"; | |
| destroy(a); | |
| assert(a.s == "A"); | |
| I i = b; | |
| destroy(i); | |
| assert(b.s == "A"); | |
| } | |
| { | |
| static bool destroyed = false; | |
| class B: I | |
| { | |
| string s = "B"; | |
| this() {} | |
| ~this() | |
| { | |
| destroyed = true; | |
| } | |
| } | |
| auto a = new B, b = new B; | |
| a.s = b.s = "asd"; | |
| destroy(a); | |
| assert(destroyed); | |
| assert(a.s == "B"); | |
| destroyed = false; | |
| I i = b; | |
| destroy(i); | |
| assert(destroyed); | |
| assert(b.s == "B"); | |
| } | |
| // this test is invalid now that the default ctor is not run after clearing | |
| version (none) | |
| { | |
| class C | |
| { | |
| string s; | |
| this() | |
| { | |
| s = "C"; | |
| } | |
| } | |
| auto a = new C; | |
| a.s = "asd"; | |
| destroy(a); | |
| assert(a.s == "C"); | |
| } | |
| } | |
| nothrow @safe @nogc unittest | |
| { | |
| { | |
| struct A { string s = "A"; } | |
| A a; | |
| a.s = "asd"; | |
| destroy!false(a); | |
| assert(a.s == "asd"); | |
| destroy(a); | |
| assert(a.s == "A"); | |
| } | |
| { | |
| static int destroyed = 0; | |
| struct C | |
| { | |
| string s = "C"; | |
| ~this() nothrow @safe @nogc | |
| { | |
| destroyed ++; | |
| } | |
| } | |
| struct B | |
| { | |
| C c; | |
| string s = "B"; | |
| ~this() nothrow @safe @nogc | |
| { | |
| destroyed ++; | |
| } | |
| } | |
| B a; | |
| a.s = "asd"; | |
| a.c.s = "jkl"; | |
| destroy!false(a); | |
| assert(destroyed == 2); | |
| assert(a.s == "asd"); | |
| assert(a.c.s == "jkl" ); | |
| destroy(a); | |
| assert(destroyed == 4); | |
| assert(a.s == "B"); | |
| assert(a.c.s == "C" ); | |
| } | |
| } | |
| nothrow unittest | |
| { | |
| // Bugzilla 20049: Test to ensure proper behavior of `nothrow` destructors | |
| class C | |
| { | |
| static int dtorCount = 0; | |
| this() nothrow {} | |
| ~this() nothrow { dtorCount++; } | |
| } | |
| auto c = new C; | |
| destroy(c); | |
| assert(C.dtorCount == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=22832 | |
| nothrow unittest | |
| { | |
| static struct A {} | |
| static class B | |
| { | |
| A opCast(T : A)() { return A(); } | |
| } | |
| destroy(B.init); | |
| } | |
| // make sure destroy!false skips re-initialization | |
| unittest | |
| { | |
| static struct S { int x; } | |
| static class C { int x; } | |
| static extern(C++) class Cpp { int x; } | |
| static void test(T)(T inst) | |
| { | |
| inst.x = 123; | |
| destroy!false(inst); | |
| assert(inst.x == 123, T.stringof); | |
| } | |
| test(S()); | |
| test(new C()); | |
| test(new Cpp()); | |
| } | |
| /// ditto | |
| void destroy(bool initialize = true, T)(ref T obj) | |
| if (__traits(isStaticArray, T)) | |
| { | |
| foreach_reverse (ref e; obj[]) | |
| destroy!initialize(e); | |
| } | |
| @safe unittest | |
| { | |
| int[2] a; | |
| a[0] = 1; | |
| a[1] = 2; | |
| destroy!false(a); | |
| assert(a == [ 1, 2 ]); | |
| destroy(a); | |
| assert(a == [ 0, 0 ]); | |
| } | |
| @safe unittest | |
| { | |
| static struct vec2f { | |
| float[2] values; | |
| alias values this; | |
| } | |
| vec2f v; | |
| destroy!(true, vec2f)(v); | |
| } | |
| @system unittest | |
| { | |
| // Bugzilla 15009 | |
| static string op; | |
| static struct S | |
| { | |
| int x; | |
| this(int x) { op ~= "C" ~ cast(char)('0'+x); this.x = x; } | |
| this(this) { op ~= "P" ~ cast(char)('0'+x); } | |
| ~this() { op ~= "D" ~ cast(char)('0'+x); } | |
| } | |
| { | |
| S[2] a1 = [S(1), S(2)]; | |
| op = ""; | |
| } | |
| assert(op == "D2D1"); // built-in scope destruction | |
| { | |
| S[2] a1 = [S(1), S(2)]; | |
| op = ""; | |
| destroy(a1); | |
| assert(op == "D2D1"); // consistent with built-in behavior | |
| } | |
| { | |
| S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]]; | |
| op = ""; | |
| } | |
| assert(op == "D4D3D2D1"); | |
| { | |
| S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]]; | |
| op = ""; | |
| destroy(a2); | |
| assert(op == "D4D3D2D1", op); | |
| } | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=19218 | |
| @system unittest | |
| { | |
| static struct S | |
| { | |
| static dtorCount = 0; | |
| ~this() { ++dtorCount; } | |
| } | |
| static interface I | |
| { | |
| ref S[3] getArray(); | |
| alias getArray this; | |
| } | |
| static class C : I | |
| { | |
| static dtorCount = 0; | |
| ~this() { ++dtorCount; } | |
| S[3] a; | |
| alias a this; | |
| ref S[3] getArray() { return a; } | |
| } | |
| C c = new C(); | |
| destroy(c); | |
| assert(S.dtorCount == 3); | |
| assert(C.dtorCount == 1); | |
| I i = new C(); | |
| destroy(i); | |
| assert(S.dtorCount == 6); | |
| assert(C.dtorCount == 2); | |
| } | |
| /// ditto | |
| void destroy(bool initialize = true, T)(ref T obj) | |
| if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T)) | |
| { | |
| static if (initialize) | |
| obj = T.init; | |
| } | |
| @safe unittest | |
| { | |
| { | |
| int a = 42; | |
| destroy!false(a); | |
| assert(a == 42); | |
| destroy(a); | |
| assert(a == 0); | |
| } | |
| { | |
| float a = 42; | |
| destroy!false(a); | |
| assert(a == 42); | |
| destroy(a); | |
| assert(a != a); // isnan | |
| } | |
| } | |
| @safe unittest | |
| { | |
| // Bugzilla 14746 | |
| static struct HasDtor | |
| { | |
| ~this() { assert(0); } | |
| } | |
| static struct Owner | |
| { | |
| HasDtor* ptr; | |
| alias ptr this; | |
| } | |
| Owner o; | |
| assert(o.ptr is null); | |
| destroy(o); // must not reach in HasDtor.__dtor() | |
| } | |
| /* ************************************************************************ | |
| COMPILER SUPPORT | |
| The compiler lowers certain expressions to instantiations of the following | |
| templates. They must be implicitly imported, which is why they are here | |
| in this file. They must also be `public` as they must be visible from the | |
| scope in which they are instantiated. They are explicitly undocumented as | |
| they are only intended to be instantiated by the compiler, not the user. | |
| **************************************************************************/ | |
| public import core.internal.entrypoint : _d_cmain; | |
| public import core.internal.array.appending : _d_arrayappendT; | |
| version (D_ProfileGC) | |
| { | |
| public import core.internal.array.appending : _d_arrayappendTTrace; | |
| public import core.internal.array.appending : _d_arrayappendcTXTrace; | |
| public import core.internal.array.concatenation : _d_arraycatnTXTrace; | |
| public import core.lifetime : _d_newitemTTrace; | |
| public import core.internal.array.construction : _d_newarrayTTrace; | |
| public import core.internal.array.construction : _d_newarrayUTrace; | |
| public import core.internal.array.construction : _d_newarraymTXTrace; | |
| public import core.internal.array.capacity: _d_arraysetlengthTTrace; | |
| public import core.internal.array.construction : _d_arrayliteralTXTrace; | |
| } | |
| public import core.internal.array.appending : _d_arrayappendcTX; | |
| public import core.internal.array.comparison : __cmp; | |
| public import core.internal.array.equality : __equals; | |
| public import core.internal.array.casting: __ArrayCast; | |
| public import core.internal.array.concatenation : _d_arraycatnTX; | |
| public import core.internal.array.construction : _d_arrayctor; | |
| public import core.internal.array.construction : _d_arraysetctor; | |
| public import core.internal.array.construction : _d_newarrayT; | |
| public import core.internal.array.construction : _d_newarrayU; | |
| public import core.internal.array.construction : _d_newarraymTX; | |
| public import core.internal.array.construction : _d_arrayliteralTX; | |
| public import core.internal.array.arrayassign : _d_arrayassign_l; | |
| public import core.internal.array.arrayassign : _d_arrayassign_r; | |
| public import core.internal.array.arrayassign : _d_arraysetassign; | |
| public import core.internal.array.capacity : _d_arraysetlengthT; | |
| public import core.internal.cast_: _d_cast; | |
| public import core.internal.dassert: _d_assert_fail; | |
| public import core.internal.destruction: __ArrayDtor; | |
| public import core.internal.moving: __move_post_blt; | |
| public import core.internal.postblit: __ArrayPostblit; | |
| public import core.internal.switch_: __switch; | |
| public import core.internal.switch_: __switch_error; | |
| public import core.lifetime : _d_delstructImpl; | |
| public import core.lifetime : _d_newThrowable; | |
| public import core.lifetime : _d_newclassT; | |
| public import core.lifetime : _d_newclassTTrace; | |
| public import core.lifetime : _d_newitemT; | |
| public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable); | |
| // Compare class and interface objects for ordering. | |
| int __cmp(C1, C2)(C1 lhs, C2 rhs) | |
| if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) && | |
| (is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D")))) | |
| { | |
| static if (is(C1 == typeof(null)) && is(C2 == typeof(null))) | |
| { | |
| return 0; | |
| } | |
| else static if (is(C1 == typeof(null))) | |
| { | |
| // Regard null references as always being "less than" | |
| return -1; | |
| } | |
| else static if (is(C2 == typeof(null))) | |
| { | |
| return 1; | |
| } | |
| else | |
| { | |
| if (lhs is rhs) | |
| return 0; | |
| if (lhs is null) | |
| return -1; | |
| if (rhs is null) | |
| return 1; | |
| return lhs.opCmp(rhs); | |
| } | |
| } | |
| // objects | |
| @safe unittest | |
| { | |
| class C | |
| { | |
| int i; | |
| this(int i) { this.i = i; } | |
| override int opCmp(Object c) const @safe | |
| { | |
| return i - (cast(C)c).i; | |
| } | |
| } | |
| auto c1 = new C(1); | |
| auto c2 = new C(2); | |
| assert(__cmp(c1, null) > 0); | |
| assert(__cmp(null, c1) < 0); | |
| assert(__cmp(c1, c1) == 0); | |
| assert(__cmp(c1, c2) < 0); | |
| assert(__cmp(c2, c1) > 0); | |
| assert(__cmp([c1, c1][], [c2, c2][]) < 0); | |
| assert(__cmp([c2, c2], [c1, c1]) > 0); | |
| } | |
| // structs | |
| @safe unittest | |
| { | |
| struct C | |
| { | |
| ubyte i; | |
| this(ubyte i) { this.i = i; } | |
| } | |
| auto c1 = C(1); | |
| auto c2 = C(2); | |
| assert(__cmp([c1, c1][], [c2, c2][]) < 0); | |
| assert(__cmp([c2, c2], [c1, c1]) > 0); | |
| assert(__cmp([c2, c2], [c2, c1]) > 0); | |
| } | |
| @safe unittest | |
| { | |
| auto a = "hello"c; | |
| assert(a > "hel"); | |
| assert(a >= "hel"); | |
| assert(a < "helloo"); | |
| assert(a <= "helloo"); | |
| assert(a > "betty"); | |
| assert(a >= "betty"); | |
| assert(a == "hello"); | |
| assert(a <= "hello"); | |
| assert(a >= "hello"); | |
| assert(a < "я"); | |
| } | |
| // Used in Exception Handling LSDA tables to 'wrap' C++ type info | |
| // so it can be distinguished from D TypeInfo | |
| class __cpp_type_info_ptr | |
| { | |
| void* ptr; // opaque pointer to C++ RTTI type info | |
| } | |
| // Compiler hook into the runtime implementation of array (vector) operations. | |
| template _arrayOp(Args...) | |
| { | |
| import core.internal.array.operations; | |
| alias _arrayOp = arrayOp!Args; | |
| } | |
| public import core.builtins : __ctfeWrite; | |
| /** | |
| Provides an "inline import", i.e. an `import` that is only available for a | |
| limited lookup. For example: | |
| --- | |
| void fun(imported!"std.stdio".File input) | |
| { | |
| ... use File from std.stdio normally ... | |
| } | |
| --- | |
| There is no need to import `std.stdio` at top level, so `fun` carries its own | |
| dependencies. The same approach can be used for template constraints: | |
| --- | |
| void fun(T)(imported!"std.stdio".File input, T value) | |
| if (imported!"std.traits".isIntegral!T) | |
| { | |
| ... | |
| } | |
| --- | |
| An inline import may be used in conjunction with the `with` statement as well. | |
| Inside the scope controlled by `with`, all symbols in the imported module are | |
| made available: | |
| --- | |
| void fun() | |
| { | |
| with (imported!"std.datetime") | |
| with (imported!"std.stdio") | |
| { | |
| Clock.currTime.writeln; | |
| } | |
| } | |
| --- | |
| The advantages of inline imports over top-level uses of the `import` declaration | |
| are the following: | |
| $(UL | |
| $(LI The `imported` template specifies dependencies at declaration level, not at | |
| module level. This allows reasoning about the dependency cost of declarations in | |
| separation instead of aggregated at module level.) | |
| $(LI Declarations using `imported` are easier to move around because they don't | |
| require top-level context, making for simpler and quicker refactorings.) | |
| $(LI Declarations using `imported` scale better with templates. This is because | |
| templates that are not instantiated do not have their parameters and constraints | |
| instantiated, so additional modules are not imported without necessity. This | |
| makes the cost of unused templates negligible. Dependencies are pulled on a need | |
| basis depending on the declarations used by client code.) | |
| ) | |
| The use of `imported` also has drawbacks: | |
| $(UL | |
| $(LI If most declarations in a module need the same imports, then factoring them | |
| at top level, outside the declarations, is simpler than repeating them.) | |
| $(LI Traditional dependency-tracking tools such as make and other build systems | |
| assume file-level dependencies and need special tooling (such as rdmd) in order | |
| to work efficiently.) | |
| $(LI Dependencies at the top of a module are easier to inspect quickly than | |
| dependencies spread throughout the module.) | |
| ) | |
| See_Also: The $(HTTP forum.dlang.org/post/tzqzmqhankrkbrfsrmbo@forum.dlang.org, | |
| forum discussion) that led to the creation of the `imported` facility. Credit is | |
| due to Daniel Nielsen and Dominikus Dittes Scherkl. | |
| */ | |
| template imported(string moduleName) | |
| { | |
| mixin("import imported = " ~ moduleName ~ ";"); | |
| } |