diff --git a/src/object.d b/src/object.d index 0cf4548c87..08f23ff81c 100644 --- a/src/object.d +++ b/src/object.d @@ -10,14 +10,12 @@ module object; -private +version(D_BetterC) { } +else { - extern (C) Object _d_newclass(const TypeInfo_Class ci); - extern (C) void rt_finalize(void *data, bool det=true); + version = Not_BetterC; } -public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable); - // NOTE: For some reason, this declaration method doesn't work // in this particular file (and this file only). It must // be a DMD thing. @@ -46,1330 +44,1487 @@ alias dstring = immutable(dchar)[]; version (D_ObjectiveC) public import core.attribute : selector; -/** - * All D class objects inherit from Object. - */ -class Object +int __cmp(T)(const T[] lhs, const T[] rhs) @trusted + if (__traits(isScalar, T)) { - /** - * Convert Object to a human readable string. - */ - string toString() - { - return typeid(this).name; - } + // Compute U as the implementation type for T + static if (is(T == ubyte) || is(T == void) || is(T == bool)) + alias U = char; + else static if (is(T == wchar)) + alias U = ushort; + else static if (is(T == dchar)) + alias U = uint; + else static if (is(T == ifloat)) + alias U = float; + else static if (is(T == idouble)) + alias U = double; + else static if (is(T == ireal)) + alias U = real; + else + alias U = T; - /** - * Compute hash function for Object. - */ - size_t toHash() @trusted nothrow + static if (is(U == char)) { - // BUG: this prevents a compacting GC from working, needs to be fixed - return cast(size_t)cast(void*)this; + import core.internal.string : dstrcmp; + return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); } - - /** - * 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) + else static if (!is(U == T)) { - // 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; + // Reuse another implementation + return __cmp(cast(U[]) lhs, cast(U[]) rhs); } - - /** - * Test whether $(D this) is equal to $(D o). - * The default implementation only compares by identity (using the $(D is) operator). - * Generally, overrides for $(D opEquals) should attempt to compare objects by their contents. - */ - bool opEquals(Object o) + else { - return this is o; + immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; + foreach (const u; 0 .. len) + { + static if (__traits(isFloating, T)) + { + immutable a = lhs.ptr[u], b = rhs.ptr[u]; + static if (is(T == cfloat) || is(T == cdouble) + || is(T == creal)) + { + // Use rt.cmath2._Ccmp instead ? + auto r = (a.re > b.re) - (a.re < b.re); + if (!r) r = (a.im > b.im) - (a.im < b.im); + } + else + { + const r = (a > b) - (a < b); + } + if (r) return r; + } + else if (lhs.ptr[u] != rhs.ptr[u]) + return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1; + } + return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length); } +} - interface Monitor +version(Not_BetterC) +{ + // Compare class and interface objects for ordering. + private int __cmp(Obj)(Obj lhs, Obj rhs) + if (is(Obj : Object)) { - void lock(); - void unlock(); + if (lhs is rhs) + return 0; + // Regard null references as always being "less than" + if (!lhs) + return -1; + if (!rhs) + return 1; + return lhs.opCmp(rhs); } - /** - * 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) + // This function is called by the compiler when dealing with array + // comparisons in the semantic analysis phase of CmpExp. The ordering + // comparison is lowered to a call to this template. + int __cmp(T1, T2)(T1[] s1, T2[] s2) + if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) { - auto ci = TypeInfo_Class.find(classname); - if (ci) + import core.internal.traits : Unqual; + alias U1 = Unqual!T1; + alias U2 = Unqual!T2; + + static if (is(U1 == void) && is(U2 == void)) + static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; } + else + static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } + + // All unsigned byte-wide types = > dstrcmp + immutable len = s1.length <= s2.length ? s1.length : s2.length; + + foreach (const u; 0 .. len) { - return ci.create(); + static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) + { + auto c = __cmp(at(s1, u), at(s2, u)); + if (c != 0) + return c; + } + else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) + { + auto c = at(s1, u).opCmp(at(s2, u)); + if (c != 0) + return c; + } + else static if (__traits(compiles, at(s1, u) < at(s2, u))) + { + if (at(s1, u) != at(s2, u)) + return at(s1, u) < at(s2, u) ? -1 : 1; + } + else + { + // TODO: fix this legacy bad behavior, see + // https://issues.dlang.org/show_bug.cgi?id=17244 + static assert(is(U1 == U2), "Internal error."); + import core.stdc.string : memcmp; + auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))(); + if (c != 0) + return c; + } } - return null; + return s1.length < s2.length ? -1 : (s1.length > s2.length); } -} -auto opEquals(Object lhs, Object rhs) -{ - // If aliased to the same object or both null => equal - if (lhs is rhs) return true; + // integral types + @safe unittest + { + void compareMinMax(T)() + { + T[2] a = [T.max, T.max]; + T[2] b = [T.min, T.min]; - // If either is null => non-equal - if (lhs is null || rhs is null) return false; + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); + } - // 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 - (issue 7147). But CTFE also guarantees that equal TypeInfos are - always identical. So, no opEquals needed during CTFE. */ - { - return lhs.opEquals(rhs); + compareMinMax!int; + compareMinMax!uint; + compareMinMax!long; + compareMinMax!ulong; + compareMinMax!short; + compareMinMax!ushort; + compareMinMax!byte; + compareMinMax!dchar; + compareMinMax!wchar; } - // General case => symmetric calls to method opEquals - return lhs.opEquals(rhs) && rhs.opEquals(lhs); -} + // char types (dstrcmp) + @safe unittest + { + void compareMinMax(T)() + { + T[2] a = [T.max, T.max]; + T[2] b = [T.min, T.min]; -/************************ -* Returns true if lhs and rhs are equal. -*/ -auto opEquals(const Object lhs, const Object rhs) -{ - // A hack for the moment. - return opEquals(cast()lhs, cast()rhs); -} + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); + } -private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow; + compareMinMax!ubyte; + compareMinMax!bool; + compareMinMax!char; + compareMinMax!(const char); -void setSameMutex(shared Object ownee, shared Object owner) -{ - _d_setSameMutex(ownee, owner); -} + string s1 = "aaaa"; + string s2 = "bbbb"; + assert(__cmp(s2, s1) > 0); + assert(__cmp(s1, s2) < 0); + } -/** - * Information about an interface. - * When an object is accessed via an interface, an Interface* appears as the - * first entry in its vtbl. - */ -struct Interface -{ - TypeInfo_Class classinfo; /// .classinfo for this interface (not for containing class) - void*[] vtbl; - size_t offset; /// offset to Interface 'this' from Object 'this' -} + // fp types + @safe unittest + { + void compareMinMax(T)() + { + T[2] a = [T.max, T.max]; + T[2] b = [T.min_normal, T.min_normal]; + T[2] c = [T.max, T.min_normal]; + T[1] d = [T.max]; + + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); + assert(__cmp(a, c) > 0); + assert(__cmp(a, d) > 0); + assert(__cmp(d, c) < 0); + assert(__cmp(c, c) == 0); + } -/** - * 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 -} + compareMinMax!real; + compareMinMax!float; + compareMinMax!double; + compareMinMax!ireal; + compareMinMax!ifloat; + compareMinMax!idouble; + compareMinMax!creal; + //compareMinMax!cfloat; + compareMinMax!cdouble; -/** - * Runtime type information about a type. - * Can be retrieved for any type using a - * $(GLINK2 expression,TypeidExpression, TypeidExpression). - */ -class TypeInfo -{ - override string toString() const pure @safe nothrow - { - return typeid(this).name; + // qualifiers + compareMinMax!(const real); + compareMinMax!(immutable real); } - override size_t toHash() @trusted const nothrow + // void[] + @safe unittest { - import core.internal.traits : externDFunc; - alias hashOf = externDFunc!("rt.util.hash.hashOf", - size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); - return hashOf(this.toString(), 0); + void[] a; + const(void)[] b; + + (() @trusted + { + a = cast(void[]) "bb"; + b = cast(const(void)[]) "aa"; + })(); + + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); } - override int opCmp(Object o) + // arrays of arrays with mixed modifiers + @safe unittest { - import core.internal.string : dstrcmp; + // https://issues.dlang.org/show_bug.cgi?id=17876 + bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; } + bool less2(const void[][] a, void[][] b) { return a < b; } + bool less3(inout size_t[][] a, size_t[][] b) { return a < b; } - if (this is o) - return 0; - TypeInfo ti = cast(TypeInfo)o; - if (ti is null) - return 1; - return dstrcmp(this.toString(), ti.toString()); + immutable size_t[][] a = [[1, 2], [3, 4]]; + size_t[][] b = [[1, 2], [3, 5]]; + assert(less1(a, b)); + assert(less3(a, b)); + + auto va = [cast(immutable void[])a[0], a[1]]; + auto vb = [cast(void[])b[0], b[1]]; + assert(less2(va, vb)); } - override bool opEquals(Object o) + // objects + @safe unittest { - /* TypeInfo instances are singletons, but duplicates can exist - * across DLL's. Therefore, comparing for a name match is - * sufficient. - */ - if (this is o) - return true; - auto ti = cast(const TypeInfo)o; - return ti && this.toString() == ti.toString(); + 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); } - /** - * 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 { return cast(size_t)p; } - - /// 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 + // structs + @safe unittest { - immutable size_t n = tsize; - for (size_t i = 0; i < n; i++) + struct C { - byte t = (cast(byte *)p1)[i]; - (cast(byte*)p1)[i] = (cast(byte*)p2)[i]; - (cast(byte*)p2)[i] = t; + ubyte i; + this(ubyte i) { this.i = i; } } - } - - /** 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 XMM register */ - @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; } + auto c1 = C(1); + auto c2 = C(2); - /** Return internal info on arguments fitting into 8byte. - * See X86-64 ABI 3.2.3 - */ - version (X86_64) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow - { - arg1 = this; - return 0; + assert(__cmp([c1, c1][], [c2, c2][]) < 0); + assert(__cmp([c2, c2], [c1, c1]) > 0); + assert(__cmp([c2, c2], [c2, c1]) > 0); } - - /** Return info used by the garbage collector to do precise collection. - */ - @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return null; } } -class TypeInfo_Enum : TypeInfo +// `lhs == rhs` lowers to `__equals(lhs, rhs)` for dynamic arrays +bool __equals(T1, T2)(T1[] lhs, T2[] rhs) { - override string toString() const { return name; } + import core.internal.traits : Unqual; + alias U1 = Unqual!T1; + alias U2 = Unqual!T2; - 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; - } + static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } + static @trusted R trustedCast(R, S)(S[] r) { return cast(R) r; } - 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); } + if (lhs.length != rhs.length) + return false; - override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } - override @property uint flags() nothrow pure const { return base.flags; } + if (lhs.length == 0 && rhs.length == 0) + return true; - override const(void)[] initializer() const + static if (is(U1 == void) && is(U2 == void)) { - return m_init.length ? m_init : base.initializer(); + return __equals(trustedCast!(ubyte[])(lhs), trustedCast!(ubyte[])(rhs)); } - - override @property size_t talign() nothrow pure const { return base.talign; } - - version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + else static if (is(U1 == void)) { - return base.argTypes(arg1, arg2); + return __equals(trustedCast!(ubyte[])(lhs), rhs); } - - override @property immutable(void)* rtInfo() const { return base.rtInfo; } - - TypeInfo base; - string name; - void[] m_init; -} - -unittest // issue 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) + else static if (is(U2 == void)) { - if (this is o) - return true; - auto c = cast(const TypeInfo_Pointer)o; - return c && this.m_next == c.m_next; + return __equals(lhs, trustedCast!(ubyte[])(rhs)); } - - override size_t getHash(scope const void* p) @trusted const + else static if (!is(U1 == U2)) { - return cast(size_t)*cast(void**)p; + // This should replace src/object.d _ArrayEq which + // compares arrays of different types such as long & int, + // char & wchar. + // Compiler lowers to __ArrayEq in dmd/src/opover.d + foreach (const u; 0 .. lhs.length) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + return true; } - - override bool equals(in void* p1, in void* p2) const + else static if (__traits(isIntegral, U1)) { - return *cast(void**)p1 == *cast(void**)p2; - } - override int compare(in void* p1, in void* p2) const - { - if (*cast(void**)p1 < *cast(void**)p2) - return -1; - else if (*cast(void**)p1 > *cast(void**)p2) - return 1; + if (!__ctfe) + { + import core.stdc.string : memcmp; + return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }(); + } else - return 0; + { + foreach (const u; 0 .. lhs.length) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + return true; + } } - - override @property size_t tsize() nothrow pure const + else { - return (void*).sizeof; - } + foreach (const u; 0 .. lhs.length) + { + static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u)))) + { + if (!__equals(at(lhs, u), at(rhs, u))) + return false; + } + else static if (__traits(isFloating, U1)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else static if (is(U1 : Object) && is(U2 : Object)) + { + if (!(cast(Object)at(lhs, u) is cast(Object)at(rhs, u) + || at(lhs, u) && (cast(Object)at(lhs, u)).opEquals(cast(Object)at(rhs, u)))) + return false; + } + else static if (__traits(hasMember, U1, "opEquals")) + { + if (!at(lhs, u).opEquals(at(rhs, u))) + return false; + } + else static if (is(U1 == delegate)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else static if (is(U1 == U11*, U11)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else static if (__traits(isAssociativeArray, U1)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else + { + if (at(lhs, u).tupleof != at(rhs, u).tupleof) + return false; + } + } - override const(void)[] initializer() const @trusted - { - return (cast(void *)null)[0 .. (void*).sizeof]; + return true; } +} - override void swap(void* p1, void* p2) const +unittest { + assert(__equals([], [])); + assert(!__equals([1, 2], [1, 2, 3])); +} + +unittest +{ + struct A { - void* tmp = *cast(void**)p1; - *cast(void**)p1 = *cast(void**)p2; - *cast(void**)p2 = tmp; + int a; } - override @property inout(TypeInfo) next() nothrow pure inout { return m_next; } - override @property uint flags() nothrow pure const { return 1; } + auto arr1 = [A(0), A(2)]; + auto arr2 = [A(0), A(1)]; + auto arr3 = [A(0), A(1)]; - TypeInfo m_next; + assert(arr1 != arr2); + assert(arr2 == arr3); } -class TypeInfo_Array : TypeInfo +unittest { - override string toString() const { return value.toString() ~ "[]"; } - - override bool opEquals(Object o) + struct A { - if (this is o) - return true; - auto c = cast(const TypeInfo_Array)o; - return c && this.value == c.value; - } + int a; + int b; - 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++) + bool opEquals(const A other) { - if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz)) - return false; + return this.a == other.b && this.b == other.a; } - return true; } - override int compare(in void* p1, in void* p2) const + auto arr1 = [A(1, 0), A(0, 1)]; + auto arr2 = [A(1, 0), A(0, 1)]; + auto arr3 = [A(0, 1), A(1, 0)]; + + assert(arr1 != arr2); + assert(arr2 == arr3); +} + +// https://issues.dlang.org/show_bug.cgi?id=18252 +unittest +{ + string[int][] a1, a2; + assert(__equals(a1, a2)); + assert(a1 == a2); + a1 ~= [0: "zero"]; + a2 ~= [0: "zero"]; + assert(__equals(a1, a2)); + assert(a1 == a2); + a2[0][1] = "one"; + assert(!__equals(a1, a2)); + assert(a1 != a2); +} + +/** +Destroys the given object and sets it back to its 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. +*/ +void destroy(T)(ref T obj) if (is(T == struct)) +{ + // We need to re-initialize `obj`. Previously, the code + // `auto init = cast(ubyte[])typeid(T).initializer()` was used, but + // `typeid` is a runtime call and requires the `TypeInfo` object which is + // not usable when compiling with -betterC. If we do `obj = T.init` then we + // end up needlessly calling postblits and destructors. So, we create a + // static immutable lvalue that can be re-used with subsequent calls to `destroy` + shared static immutable T init = T.init; + + _destructRecurse(obj); + () @trusted { + auto dest = (cast(ubyte*) &obj)[0 .. T.sizeof]; + auto src = (cast(ubyte*) &init)[0 .. T.sizeof]; + dest[] = src[]; + } (); +} + +private void _destructRecurse(S)(ref S s) + if (is(S == struct)) +{ + static if (__traits(hasMember, S, "__xdtor") && + // Bugzilla 14746: Check that it's the exact member of S. + __traits(isSame, S, __traits(parent, s.__xdtor))) + s.__xdtor(); +} + +nothrow @safe @nogc unittest +{ { - void[] a1 = *cast(void[]*)p1; - void[] a2 = *cast(void[]*)p2; - size_t sz = value.tsize; - size_t len = a1.length; + struct A { string s = "A"; } + A a; + a.s = "asd"; + destroy(a); + assert(a.s == "A"); + } + { + static int destroyed = 0; + struct C + { + string s = "C"; + ~this() nothrow @safe @nogc + { + destroyed ++; + } + } - if (a2.length < len) - len = a2.length; - for (size_t u = 0; u < len; u++) + struct B { - immutable int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz); - if (result) - return result; + C c; + string s = "B"; + ~this() nothrow @safe @nogc + { + destroyed ++; + } } - return cast(int)a1.length - cast(int)a2.length; + B a; + a.s = "asd"; + a.c.s = "jkl"; + destroy(a); + assert(destroyed == 2); + assert(a.s == "B"); + assert(a.c.s == "C" ); } +} - override @property size_t tsize() nothrow pure const +version(Not_BetterC) +{ + /// ditto + void destroy(T)(T obj) if (is(T == class)) { - return (void[]).sizeof; + static if(__traits(getLinkage, T) == "C++") + { + obj.__xdtor(); + + enum classSize = __traits(classInstanceSize, T); + (cast(void*)obj)[0 .. classSize] = typeid(T).initializer[]; + } + else + rt_finalize(cast(void*)obj); } - override const(void)[] initializer() const @trusted + /// ditto + void destroy(T)(T obj) if (is(T == interface)) { - return (cast(void *)null)[0 .. (void[]).sizeof]; + destroy(cast(Object)obj); } - override void swap(void* p1, void* p2) const + /// Reference type demonstration + unittest { - void[] tmp = *cast(void[]*)p1; - *cast(void[]*)p1 = *cast(void[]*)p2; - *cast(void[]*)p2 = tmp; - } + class C + { + struct Agg + { + static int dtorCount; - TypeInfo value; + int x = 10; + ~this() { dtorCount++; } + } - override @property inout(TypeInfo) next() nothrow pure inout - { - return value; - } + static int dtorCount; - override @property uint flags() nothrow pure const { return 1; } + string s = "S"; + Agg a; + ~this() { dtorCount++; } + } - override @property size_t talign() nothrow pure const - { - return (void[]).alignof; - } + 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` + + // check C++ classes work too! + extern (C++) class CPP + { + struct Agg + { + __gshared int dtorCount; - version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) - { - arg1 = typeid(size_t); - arg2 = typeid(void*); - return 0; - } -} + int x = 10; + ~this() { dtorCount++; } + } -class TypeInfo_StaticArray : TypeInfo -{ - override string toString() const - { - import core.internal.string : unsignedToTempString; + __gshared int dtorCount; - char[20] tmpBuff = void; - return value.toString() ~ "[" ~ unsignedToTempString(len, tmpBuff, 10) ~ "]"; - } + string s = "S"; + Agg a; + ~this() { dtorCount++; } + } - 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; + 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(cpp); + assert(cpp.dtorCount == 1); // `cpp`'s destructor was called + assert(cpp.s == "S"); // `cpp.s` is back to its inital state, `"S"` + assert(cpp.a.dtorCount == 1); // `cpp.a`'s destructor was called + assert(cpp.a.x == 10); // `cpp.a.x` is back to its inital state, `10` } - override size_t getHash(scope const void* p) @trusted const + /// Value type demonstration + unittest { - return getArrayHash(value, p, len); + int i; + assert(i == 0); // `i`'s initial state is `0` + i = 1; + assert(i == 1); // `i` changed to `1` + destroy(i); + assert(i == 0); // `i` is back to its initial state `0` } - override bool equals(in void* p1, in void* p2) const + unittest { - size_t sz = value.tsize; - - for (size_t u = 0; u < len; u++) + interface I { } { - if (!value.equals(p1 + u * sz, p2 + u * sz)) - return false; + 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"); } - return true; } - override int compare(in void* p1, in void* p2) const + nothrow @safe @nogc unittest { - 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; + struct A { string s = "A"; } + A a; + 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(a); + assert(destroyed == 2); + assert(a.s == "B"); + assert(a.c.s == "C" ); } - return 0; } - override @property size_t tsize() nothrow pure const + /// ditto + void destroy(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct)) { - return len * value.tsize; + foreach_reverse (ref e; obj[]) + destroy(e); } - override void swap(void* p1, void* p2) const + unittest { - import core.memory; - import core.stdc.string : memcpy; - - void* tmp; - size_t sz = value.tsize; - ubyte[16] buffer; - void* pbuffer; - - if (sz < buffer.sizeof) - tmp = buffer.ptr; - else - tmp = pbuffer = (new void[sz]).ptr; - - for (size_t u = 0; u < len; u += sz) - { - size_t o = u * sz; - memcpy(tmp, p1 + o, sz); - memcpy(p1 + o, p2 + o, sz); - memcpy(p2 + o, tmp, sz); - } - if (pbuffer) - GC.free(pbuffer); + int[2] a; + a[0] = 1; + a[1] = 2; + destroy(a); + assert(a == [ 0, 0 ]); } - override const(void)[] initializer() nothrow pure const + unittest { - return value.initializer(); - } + static struct vec2f { + float[2] values; + alias values this; + } - override @property inout(TypeInfo) next() nothrow pure inout { return value; } - override @property uint flags() nothrow pure const { return value.flags; } + vec2f v; + destroy!vec2f(v); + } - override void destroy(void* p) const + unittest { - immutable sz = value.tsize; - p += sz * len; - foreach (i; 0 .. len) + // Bugzilla 15009 + static string op; + static struct S { - p -= sz; - value.destroy(p); + 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); } } - } - override void postblit(void* p) const - { - immutable sz = value.tsize; - foreach (i; 0 .. len) { - value.postblit(p); - p += sz; + 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 } - } - TypeInfo value; - size_t len; + { + 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); + } + } - override @property size_t talign() nothrow pure const + /// ditto + void destroy(T)(ref T obj) + if (!is(T == struct) && !is(T == interface) && !is(T == class) && !_isStaticArray!T) { - return value.talign; + obj = T.init; } - version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + template _isStaticArray(T : U[N], U, size_t N) { - arg1 = typeid(void*); - return 0; + enum bool _isStaticArray = true; } -} -class TypeInfo_AssociativeArray : TypeInfo -{ - override string toString() const + template _isStaticArray(T) { - return value.toString() ~ "[" ~ key.toString() ~ "]"; + enum bool _isStaticArray = false; } - override bool opEquals(Object o) + unittest { - if (this is o) - return true; - auto c = cast(const TypeInfo_AssociativeArray)o; - return c && this.key == c.key && - this.value == c.value; + { + int a = 42; + destroy(a); + assert(a == 0); + } + { + float a = 42; + destroy(a); + assert(isnan(a)); + } } +} - override bool equals(in void* p1, in void* p2) @trusted const - { - return !!_aaEqual(this, *cast(const void**) p1, *cast(const void**) p2); - } +/*============================ NOT -betterC ==================================== + Anything below this line should not be imported if compiling with -betterC +*/ +version(Not_BetterC): - override hash_t getHash(scope const void* p) nothrow @trusted const - { - return _aaGetHash(cast(void*)p, this); - } +private +{ + extern (C) Object _d_newclass(const TypeInfo_Class ci); + extern (C) void rt_finalize(void *data, bool det=true); +} - // BUG: need to add the rest of the functions +public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable); - override @property size_t tsize() nothrow pure const +/** + * All D class objects inherit from Object. + */ +class Object +{ + /** + * Convert Object to a human readable string. + */ + string toString() { - return (char[int]).sizeof; + return typeid(this).name; } - override const(void)[] initializer() const @trusted + /** + * Compute hash function for Object. + */ + size_t toHash() @trusted nothrow { - return (cast(void *)null)[0 .. (char[int]).sizeof]; + // BUG: this prevents a compacting GC from working, needs to be fixed + return cast(size_t)cast(void*)this; } - override @property inout(TypeInfo) next() nothrow pure inout { return value; } - override @property uint flags() nothrow pure const { return 1; } - - TypeInfo value; - TypeInfo key; - - override @property size_t talign() nothrow pure const + /** + * 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) { - return (char[int]).alignof; - } + // BUG: this prevents a compacting GC from working, needs to be fixed + //return cast(int)cast(void*)this - cast(int)cast(void*)o; - version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) - { - arg1 = typeid(void*); - return 0; + throw new Exception("need opCmp for class " ~ typeid(this).name); + //return this !is o; } -} - -class TypeInfo_Vector : TypeInfo -{ - override string toString() const { return "__vector(" ~ base.toString() ~ ")"; } - override bool opEquals(Object o) + /** + * Test whether $(D this) is equal to $(D o). + * The default implementation only compares by identity (using the $(D is) operator). + * Generally, overrides for $(D opEquals) should attempt to compare objects by their contents. + */ + bool opEquals(Object o) { - if (this is o) - return true; - auto c = cast(const TypeInfo_Vector)o; - return c && this.base == c.base; + return this is o; } - 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 + interface Monitor { - return base.initializer(); + void lock(); + void unlock(); } - override @property size_t talign() nothrow pure const { return 16; } - - version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + /** + * 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) { - return base.argTypes(arg1, arg2); + auto ci = TypeInfo_Class.find(classname); + if (ci) + { + return ci.create(); + } + return null; } - - TypeInfo base; } -class TypeInfo_Function : TypeInfo +auto opEquals(Object lhs, Object rhs) { - override string toString() const - { - import core.demangle : demangleType; - - alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure; - SafeDemangleFunctionType demangle = ( () @trusted => cast(SafeDemangleFunctionType)(&demangleType) ) (); + // If aliased to the same object or both null => equal + if (lhs is rhs) return true; - return (() @trusted => cast(string)(demangle(deco))) (); - } + // If either is null => non-equal + if (lhs is null || rhs is null) return false; - override bool opEquals(Object o) + // 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 + (issue 7147). But CTFE also guarantees that equal TypeInfos are + always identical. So, no opEquals needed during CTFE. */ { - if (this is o) - return true; - auto c = cast(const TypeInfo_Function)o; - return c && this.deco == c.deco; + return lhs.opEquals(rhs); } - // BUG: need to add the rest of the functions - - override @property size_t tsize() nothrow pure const - { - return 0; // no size for functions - } + // General case => symmetric calls to method opEquals + return lhs.opEquals(rhs) && rhs.opEquals(lhs); +} - override const(void)[] initializer() const @safe - { - return null; - } +/************************ +* Returns true if lhs and rhs are equal. +*/ +auto opEquals(const Object lhs, const Object rhs) +{ + // A hack for the moment. + return opEquals(cast()lhs, cast()rhs); +} - TypeInfo next; +private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow; - /** - * Mangled function type string - */ - string deco; +void setSameMutex(shared Object ownee, shared Object owner) +{ + _d_setSameMutex(ownee, owner); } -unittest +/** + * Information about an interface. + * When an object is accessed via an interface, an Interface* appears as the + * first entry in its vtbl. + */ +struct Interface { - abstract class C - { - void func(); - void func(int a); - int func(int a, int b); - } + TypeInfo_Class classinfo; /// .classinfo for this interface (not for containing class) + void*[] vtbl; + size_t offset; /// offset to Interface 'this' from Object 'this' +} - alias functionTypes = typeof(__traits(getVirtualFunctions, 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)"); +/** + * 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 } -class TypeInfo_Delegate : TypeInfo +/** + * Runtime type information about a type. + * Can be retrieved for any type using a + * $(GLINK2 expression,TypeidExpression, TypeidExpression). + */ +class TypeInfo { - override string toString() const + override string toString() const pure @safe nothrow { - return cast(string)(next.toString() ~ " delegate()"); + return typeid(this).name; } - override bool opEquals(Object o) + override size_t toHash() @trusted const nothrow { - if (this is o) - return true; - auto c = cast(const TypeInfo_Delegate)o; - return c && this.deco == c.deco; + import core.internal.traits : externDFunc; + alias hashOf = externDFunc!("rt.util.hash.hashOf", + size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); + return hashOf(this.toString(), 0); } - override size_t getHash(scope const void* p) @trusted const + override int opCmp(Object o) { - return hashOf(*cast(void delegate()*)p); - } + import core.internal.string : dstrcmp; - 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; + if (this is o) + return 0; + TypeInfo ti = cast(TypeInfo)o; + if (ti is null) + return 1; + return dstrcmp(this.toString(), ti.toString()); } - override int compare(in void* p1, in void* p2) const + override bool opEquals(Object o) { - 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; + /* TypeInfo instances are singletons, but duplicates can exist + * across DLL's. Therefore, comparing for a name match is + * sufficient. + */ + if (this is o) + return true; + auto ti = cast(const TypeInfo)o; + return ti && this.toString() == ti.toString(); } - override @property size_t tsize() nothrow pure const + /** + * 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 { return cast(size_t)p; } + + /// 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 { - alias dg = int delegate(); - return dg.sizeof; + immutable size_t n = tsize; + for (size_t i = 0; i < n; i++) + { + byte t = (cast(byte *)p1)[i]; + (cast(byte*)p1)[i] = (cast(byte*)p2)[i]; + (cast(byte*)p2)[i] = t; + } } - override const(void)[] initializer() const @trusted + /** 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 XMM register */ + @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 (X86_64) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow { - return (cast(void *)null)[0 .. (int delegate()).sizeof]; + arg1 = this; + return 0; } - override @property uint flags() nothrow pure const { return 1; } + /** Return info used by the garbage collector to do precise collection. + */ + @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return null; } +} - TypeInfo next; - string deco; +class TypeInfo_Enum : TypeInfo +{ + override string toString() const { return name; } - override @property size_t talign() nothrow pure const + override bool opEquals(Object o) { - alias dg = int delegate(); - return dg.alignof; + if (this is o) + return true; + auto c = cast(const TypeInfo_Enum)o; + return c && this.name == c.name && + this.base == c.base; } - version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + 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() const { - arg1 = typeid(void*); - arg2 = typeid(void*); - return 0; + return m_init.length ? m_init : base.initializer(); } -} -unittest -{ - // Bugzilla 15367 - void f1() {} - void f2() {} + override @property size_t talign() nothrow pure const { return base.talign; } - // TypeInfo_Delegate.getHash - int[void delegate()] aa; - assert(aa.length == 0); - aa[&f1] = 1; - assert(aa.length == 1); - aa[&f1] = 1; - assert(aa.length == 1); + version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + { + return base.argTypes(arg1, arg2); + } - auto a1 = [&f2, &f1]; - auto a2 = [&f2, &f1]; + override @property immutable(void)* rtInfo() const { return base.rtInfo; } - // TypeInfo_Delegate.equals - for (auto i = 0; i < 2; i++) - assert(a1[i] == a2[i]); - assert(a1 == a2); + TypeInfo base; + string name; + void[] m_init; +} - // TypeInfo_Delegate.compare - for (auto i = 0; i < 2; i++) - assert(a1[i] <= a2[i]); - assert(a1 <= a2); +unittest // issue 12233 +{ + static assert(is(typeof(TypeInfo.init) == TypeInfo)); + assert(TypeInfo.init is null); } -/** - * Runtime type information about a class. - * Can be retrieved from an object instance by using the - * $(DDSUBLINK spec/property,classinfo, .classinfo) property. - */ -class TypeInfo_Class : TypeInfo + +// 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 info.name; } + override string toString() const { return m_next.toString() ~ "*"; } override bool opEquals(Object o) { if (this is o) return true; - auto c = cast(const TypeInfo_Class)o; - return c && this.info.name == c.info.name; + 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 { - auto o = *cast(Object*)p; - return o ? o.toHash() : 0; + return cast(size_t)*cast(void**)p; } 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)); + return *cast(void**)p1 == *cast(void**)p2; } 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; + if (*cast(void**)p1 < *cast(void**)p2) + return -1; + else if (*cast(void**)p1 > *cast(void**)p2) + return 1; + else + return 0; } override @property size_t tsize() nothrow pure const { - return Object.sizeof; + return (void*).sizeof; } - override const(void)[] initializer() nothrow pure const @safe + override const(void)[] initializer() const @trusted { - return m_init; + 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; } - override @property const(OffsetTypeInfo)[] offTi() nothrow pure const + TypeInfo m_next; +} + +class TypeInfo_Array : TypeInfo +{ + override string toString() const { return value.toString() ~ "[]"; } + + override bool opEquals(Object o) { - return m_offTi; + if (this is o) + return true; + auto c = cast(const TypeInfo_Array)o; + return c && this.value == c.value; } - @property auto info() @safe nothrow pure const { return this; } - @property auto typeinfo() @safe nothrow pure const { 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 : uint + override size_t getHash(scope const void* p) @trusted const { - isCOMclass = 0x1, - noPointers = 0x2, - hasOffTi = 0x4, - hasCtor = 0x8, - hasGetMembers = 0x10, - hasTypeInfo = 0x20, - isAbstract = 0x40, - isCPPclass = 0x80, - hasDtor = 0x100, + void[] a = *cast(void[]*)p; + return getArrayHash(value, a.ptr, a.length); } - ClassFlags m_flags; - 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; } - /** - * Search all modules for TypeInfo_Class corresponding to classname. - * Returns: null if not found - */ - static const(TypeInfo_Class) find(in char[] classname) + override bool equals(in void* p1, in void* p2) const { - foreach (m; ModuleInfo) + 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 (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; - } - } + if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz)) + return false; } - return null; + return true; } - /** - * Create instance of Object represented by 'this'. - */ - Object create() const + override int compare(in void* p1, in void* p2) 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) + 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++) { - defaultConstructor(o); + immutable int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz); + if (result) + return result; } - return o; + return cast(int)a1.length - cast(int)a2.length; } -} -alias ClassInfo = TypeInfo_Class; + override @property size_t tsize() nothrow pure const + { + return (void[]).sizeof; + } -unittest -{ - // Bugzilla 14401 - static class X + override const(void)[] initializer() const @trusted { - int a; + return (cast(void *)null)[0 .. (void[]).sizeof]; } - 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); + 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 (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + { + arg1 = typeid(size_t); + arg2 = typeid(void*); + return 0; + } } -class TypeInfo_Interface : TypeInfo +class TypeInfo_StaticArray : TypeInfo { - override string toString() const { return info.name; } + override string toString() const + { + import core.internal.string : unsignedToTempString; + + char[20] tmpBuff = void; + return value.toString() ~ "[" ~ unsignedToTempString(len, tmpBuff, 10) ~ "]"; + } 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; + 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 { - Interface* pi = **cast(Interface ***)*cast(void**)p; - Object o = cast(Object)(*cast(void**)p - pi.offset); - assert(o); - return o.toHash(); + return getArrayHash(value, p, len); } 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); + size_t sz = value.tsize; - return o1 == o2 || (o1 && o1.opCmp(o2) == 0); + 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 { - 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; + size_t sz = value.tsize; - // Regard null references as always being "less than" - if (o1 != o2) + for (size_t u = 0; u < len; u++) { - if (o1) - { - if (!o2) - c = 1; - else - c = o1.opCmp(o2); - } - else - c = -1; + immutable int result = value.compare(p1 + u * sz, p2 + u * sz); + if (result) + return result; } - return c; + return 0; } override @property size_t tsize() nothrow pure const { - return Object.sizeof; + return len * value.tsize; } - override const(void)[] initializer() const @trusted + override void swap(void* p1, void* p2) const { - return (cast(void *)null)[0 .. Object.sizeof]; - } + import core.memory; + import core.stdc.string : memcpy; - override @property uint flags() nothrow pure const { return 1; } + void* tmp; + size_t sz = value.tsize; + ubyte[16] buffer; + void* pbuffer; - TypeInfo_Class info; -} + if (sz < buffer.sizeof) + tmp = buffer.ptr; + else + tmp = pbuffer = (new void[sz]).ptr; -class TypeInfo_Struct : TypeInfo -{ - override string toString() const { return name; } + for (size_t u = 0; u < len; u += sz) + { + size_t o = u * sz; + memcpy(tmp, p1 + o, sz); + memcpy(p1 + o, p2 + o, sz); + memcpy(p2 + o, tmp, sz); + } + if (pbuffer) + GC.free(pbuffer); + } - override bool opEquals(Object o) + override const(void)[] initializer() nothrow pure const { - if (this is o) - return true; - auto s = cast(const TypeInfo_Struct)o; - return s && this.name == s.name && - this.initializer().length == s.initializer().length; + return value.initializer(); } - override size_t getHash(scope const void* p) @trusted pure nothrow const + 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 { - assert(p); - if (xtoHash) + immutable sz = value.tsize; + p += sz * len; + foreach (i; 0 .. len) { - return (*xtoHash)(p); + p -= sz; + value.destroy(p); } - else + } + + override void postblit(void* p) const + { + immutable sz = value.tsize; + foreach (i; 0 .. len) { - import core.internal.traits : externDFunc; - alias hashOf = externDFunc!("rt.util.hash.hashOf", - size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); - return hashOf(p[0 .. initializer().length], 0); + value.postblit(p); + p += sz; } } - override bool equals(in void* p1, in void* p2) @trusted pure nothrow const - { - import core.stdc.string : memcmp; + TypeInfo value; + size_t len; - if (!p1 || !p2) - return false; - else if (xopEquals) - return (*xopEquals)(p1, p2); - else if (p1 == p2) - return true; - else - // BUG: relies on the GC not moving objects - return memcmp(p1, p2, initializer().length) == 0; + override @property size_t talign() nothrow pure const + { + return value.talign; } - override int compare(in void* p1, in void* p2) @trusted pure nothrow const + version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) { - 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) - return (*xopCmp)(p2, p1); - else - // BUG: relies on the GC not moving objects - return memcmp(p1, p2, initializer().length); - } - else - return -1; - } + arg1 = typeid(void*); 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 name; - 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() const { return m_RTInfo; } - - version (X86_64) - { - 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 -} - -unittest -{ - struct S - { - bool opEquals(ref const S rhs) const - { - return false; - } - } - S s; - assert(!typeid(S).equals(&s, &s)); } -class TypeInfo_Tuple : TypeInfo +class TypeInfo_AssociativeArray : TypeInfo { - TypeInfo[] elements; - override string toString() const { - string s = "("; - foreach (i, element; elements) - { - if (i) - s ~= ','; - s ~= element.toString(); - } - s ~= ")"; - return s; + return value.toString() ~ "[" ~ key.toString() ~ "]"; } 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; + auto c = cast(const TypeInfo_AssociativeArray)o; + return c && this.key == c.key && + this.value == c.value; } - override size_t getHash(scope const void* p) const + override bool equals(in void* p1, in void* p2) @trusted const { - assert(0); + return !!_aaEqual(this, *cast(const void**) p1, *cast(const void**) p2); } - override bool equals(in void* p1, in void* p2) const + override hash_t getHash(scope const void* p) nothrow @trusted const { - assert(0); + return _aaGetHash(cast(void*)p, this); } - override int compare(in void* p1, in void* p2) const - { - assert(0); - } + // BUG: need to add the rest of the functions override @property size_t tsize() nothrow pure const { - assert(0); + return (char[int]).sizeof; } override const(void)[] initializer() const @trusted { - assert(0); - } - - override void swap(void* p1, void* p2) const - { - assert(0); + return (cast(void *)null)[0 .. (char[int]).sizeof]; } - override void destroy(void* p) const - { - assert(0); - } + override @property inout(TypeInfo) next() nothrow pure inout { return value; } + override @property uint flags() nothrow pure const { return 1; } - override void postblit(void* p) const - { - assert(0); - } + TypeInfo value; + TypeInfo key; override @property size_t talign() nothrow pure const { - assert(0); + return (char[int]).alignof; } version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) { - assert(0); + arg1 = typeid(void*); + return 0; } } -class TypeInfo_Const : TypeInfo +class TypeInfo_Vector : TypeInfo { - override string toString() const - { - return cast(string) ("const(" ~ base.toString() ~ ")"); - } + override string toString() const { return "__vector(" ~ 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); + 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 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 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; } @@ -1379,7 +1534,7 @@ class TypeInfo_Const : TypeInfo return base.initializer(); } - override @property size_t talign() nothrow pure const { return base.talign; } + override @property size_t talign() nothrow pure const { return 16; } version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) { @@ -1389,2593 +1544,2470 @@ class TypeInfo_Const : TypeInfo TypeInfo base; } -class TypeInfo_Invariant : TypeInfo_Const +class TypeInfo_Function : TypeInfo { override string toString() const { - return cast(string) ("immutable(" ~ base.toString() ~ ")"); + import core.demangle : demangleType; + + alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure; + SafeDemangleFunctionType demangle = ( () @trusted => cast(SafeDemangleFunctionType)(&demangleType) ) (); + + return (() @trusted => 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; } + + TypeInfo next; + + /** + * Mangled function type string + */ + string deco; } -class TypeInfo_Shared : TypeInfo_Const +unittest { - override string toString() const + abstract class C { - return cast(string) ("shared(" ~ base.toString() ~ ")"); + void func(); + void func(int a); + int func(int a, int b); } + + alias functionTypes = typeof(__traits(getVirtualFunctions, 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)"); } -class TypeInfo_Inout : TypeInfo_Const +class TypeInfo_Delegate : TypeInfo { override string toString() const { - return cast(string) ("inout(" ~ base.toString() ~ ")"); + return cast(string)(next.toString() ~ " delegate()"); } -} -// 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) + override bool opEquals(Object o) { - deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.") - void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; } + if (this is o) + return true; + auto c = cast(const TypeInfo_Delegate)o; + return c && this.deco == c.deco; } - else + + override size_t getHash(scope const void* p) @trusted const { - @disable this(); + return hashOf(*cast(void delegate()*)p); } -const: - private void* addrOf(int flag) nothrow pure @nogc - in + override bool equals(in void* p1, in void* p2) const { - assert(flag >= MItlsctor && flag <= MIname); - assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1)); + auto dg1 = *cast(void delegate()*)p1; + auto dg2 = *cast(void delegate()*)p2; + return dg1 == dg2; } - do - { - import core.stdc.string : strlen; - void* p = cast(void*)&this + ModuleInfo.sizeof; + override int compare(in void* p1, in void* p2) const + { + auto dg1 = *cast(void delegate()*)p1; + auto dg2 = *cast(void delegate()*)p2; - 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); + if (dg1 < dg2) + return -1; + else if (dg1 > dg2) + return 1; + else + return 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 + override @property size_t tsize() nothrow pure const { - return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null; + alias dg = int delegate(); + return dg.sizeof; } - /************************ - * Returns: - * module destructor for thread locals, `null` if there isn't one - */ - @property void function() tlsdtor() nothrow pure @nogc + override const(void)[] initializer() const @trusted { - return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null; + return (cast(void *)null)[0 .. (int delegate()).sizeof]; } - /***************************** - * Returns: - * address of a module's `const(MemberInfo)[] getMembers(string)` function, `null` if there isn't one - */ - @property void* xgetMembers() nothrow pure @nogc + override @property uint flags() nothrow pure const { return 1; } + + TypeInfo next; + string deco; + + override @property size_t talign() nothrow pure const { - return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null; + alias dg = int delegate(); + return dg.alignof; } - /************************ - * Returns: - * module constructor, `null` if there isn't one - */ - @property void function() ctor() nothrow pure @nogc + version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) { - return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null; + arg1 = typeid(void*); + arg2 = typeid(void*); + return 0; } +} - /************************ - * Returns: - * module destructor, `null` if there isn't one - */ - @property void function() dtor() nothrow pure @nogc +unittest +{ + // Bugzilla 15367 + void f1() {} + void f2() {} + + // TypeInfo_Delegate.getHash + int[void delegate()] aa; + assert(aa.length == 0); + aa[&f1] = 1; + assert(aa.length == 1); + aa[&f1] = 1; + assert(aa.length == 1); + + auto a1 = [&f2, &f1]; + auto a2 = [&f2, &f1]; + + // TypeInfo_Delegate.equals + for (auto i = 0; i < 2; i++) + assert(a1[i] == a2[i]); + assert(a1 == a2); + + // TypeInfo_Delegate.compare + for (auto i = 0; i < 2; i++) + assert(a1[i] <= a2[i]); + assert(a1 <= a2); +} + +/** + * Runtime type information about a class. + * Can be retrieved from an object instance by using the + * $(DDSUBLINK spec/property,classinfo, .classinfo) property. + */ +class TypeInfo_Class : TypeInfo +{ + override string toString() const { return info.name; } + + override bool opEquals(Object o) { - return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null; + if (this is o) + return true; + auto c = cast(const TypeInfo_Class)o; + return c && this.info.name == c.info.name; } - /************************ - * Returns: - * module order independent constructor, `null` if there isn't one - */ - @property void function() ictor() nothrow pure @nogc + override size_t getHash(scope const void* p) @trusted const { - return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null; + auto o = *cast(Object*)p; + return o ? o.toHash() : 0; } - /************* - * Returns: - * address of function that runs the module's unittests, `null` if there isn't one - */ - @property void function() unitTest() nothrow pure @nogc + override bool equals(in void* p1, in void* p2) const { - return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null; + Object o1 = *cast(Object*)p1; + Object o2 = *cast(Object*)p2; + + return (o1 is o2) || (o1 && o1.opEquals(o2)); } - /**************** - * Returns: - * array of pointers to the ModuleInfo's of modules imported by this one - */ - @property immutable(ModuleInfo*)[] importedModules() nothrow pure @nogc + override int compare(in void* p1, in void* p2) const { - if (flags & MIimportedModules) + 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) { - auto p = cast(size_t*)addrOf(MIimportedModules); - return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p]; + if (o1) + { + if (!o2) + c = 1; + else + c = o1.opCmp(o2); + } + else + c = -1; } - return null; + return c; } - /**************** - * Returns: - * array of TypeInfo_Class references for classes defined in this module - */ - @property TypeInfo_Class[] localClasses() 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() nothrow pure @nogc - { - if (true || flags & MIname) // always available for now - { - import core.stdc.string : strlen; - - auto p = cast(immutable char*)addrOf(MIname); - return p[0 .. strlen(p)]; - } - // return null; - } - - static int opApply(scope int delegate(ModuleInfo*) dg) + override @property size_t tsize() nothrow pure const { - 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)); + return Object.sizeof; } -} -unittest -{ - ModuleInfo* m1; - foreach (m; ModuleInfo) + override const(void)[] initializer() nothrow pure const @safe { - m1 = m; + return m_init; } -} - -/////////////////////////////////////////////////////////////////////////////// -// Throwable -/////////////////////////////////////////////////////////////////////////////// + override @property uint flags() nothrow pure const { return 1; } -/** - * 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 + override @property const(OffsetTypeInfo)[] offTi() nothrow pure const { - int opApply(scope int delegate(ref const(char[]))) const; - int opApply(scope int delegate(ref size_t, ref const(char[]))) const; - string toString() const; + return m_offTi; } - 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; - - /** - * 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. - */ - private Throwable nextInChain; - - 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() @safe inout return scope pure nothrow @nogc { return nextInChain; } + @property auto info() @safe nothrow pure const { return this; } + @property auto typeinfo() @safe nothrow pure const { return this; } - /** - * Replace next in chain with `tail`. - * Use `chainTogether` instead if at all possible. - */ - @property void next(Throwable tail) @safe scope pure nothrow @nogc + 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 : uint { - if (tail && tail._refcount) - ++tail._refcount; // increment the replacement *first* - - auto n = nextInChain; - nextInChain = null; // sever the tail before deleting it - - if (n && n._refcount) - _d_delThrowable(n); // now delete the old tail - - nextInChain = tail; // and set the new tail + isCOMclass = 0x1, + noPointers = 0x2, + hasOffTi = 0x4, + hasCtor = 0x8, + hasGetMembers = 0x10, + hasTypeInfo = 0x20, + isAbstract = 0x40, + isCPPclass = 0x80, + hasDtor = 0x100, } + ClassFlags m_flags; + void* deallocator; + OffsetTypeInfo[] m_offTi; + void function(Object) defaultConstructor; // default Constructor - /** - * 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 - */ - @system @nogc final pure nothrow ref uint refcount() return scope { return _refcount; } + immutable(void)* m_RTInfo; // data for precise GC + override @property immutable(void)* rtInfo() const { return m_RTInfo; } /** - * Loop over the chain of Throwables. + * Search all modules for TypeInfo_Class corresponding to classname. + * Returns: null if not found */ - int opApply(scope int delegate(Throwable) dg) + static const(TypeInfo_Class) find(in char[] classname) { - int result = 0; - for (Throwable t = this; t; t = t.nextInChain) + foreach (m; ModuleInfo) { - result = dg(t); - if (result) - break; + 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 result; + return null; } /** - * 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 + * Create instance of Object represented by 'this'. */ - static @__future @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2) + Object create() const { - if (e2 && e2.refcount()) - ++e2.refcount(); - if (!e1) - return e2; - if (!e2) - return e1; - for (auto e = e1; 1; e = e.nextInChain) + if (m_flags & 8 && !defaultConstructor) + return null; + if (m_flags & 64) // abstract + return null; + Object o = _d_newclass(this); + if (m_flags & 8 && defaultConstructor) { - if (!e.nextInChain) - { - e.nextInChain = e2; - break; - } + defaultConstructor(o); } - return e1; + return o; } +} - @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null) +alias ClassInfo = TypeInfo_Class; + +unittest +{ + // Bugzilla 14401 + static class X { - this.msg = msg; - this.nextInChain = nextInChain; - //this.info = _d_traceContext(); + int a; } - @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null) + 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 { return info.name; } + + override bool opEquals(Object o) { - this(msg, nextInChain); - this.file = file; - this.line = line; - //this.info = _d_traceContext(); + if (this is o) + return true; + auto c = cast(const TypeInfo_Interface)o; + return c && this.info.name == typeid(c).name; } - @trusted nothrow ~this() + override size_t getHash(scope const void* p) @trusted const { - if (nextInChain && nextInChain._refcount) - _d_delThrowable(nextInChain); + Interface* pi = **cast(Interface ***)*cast(void**)p; + Object o = cast(Object)(*cast(void**)p - pi.offset); + assert(o); + return o.toHash(); } - /** - * 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() + override bool equals(in void* p1, in void* p2) const { - string s; - toString((buf) { s ~= buf; }); - return s; + 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); } - /** - * 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 + override int compare(in void* p1, in void* p2) const { - import core.internal.string : unsignedToTempString; - - char[20] tmpBuff = void; - - sink(typeid(this).name); - sink("@"); sink(file); - sink("("); sink(unsignedToTempString(line, tmpBuff, 10)); sink(")"); + 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; - if (msg.length) - { - sink(": "); sink(msg); - } - if (info) + // Regard null references as always being "less than" + if (o1 != o2) { - try - { - sink("\n----------------"); - foreach (t; info) - { - sink("\n"); sink(t); - } - } - catch (Throwable) + if (o1) { - // ignore more errors + if (!o2) + c = 1; + else + c = o1.opCmp(o2); } + else + c = -1; } + return c; } - /** - * Get the message describing the error. - * Base behavior is to return the `Throwable.msg` field. - * Override to return some other error message. - * - * Returns: - * Error message - */ - @__future const(char)[] message() const + override @property size_t tsize() nothrow pure const { - return this.msg; + return Object.sizeof; } -} + override const(void)[] initializer() const @trusted + { + return (cast(void *)null)[0 .. Object.sizeof]; + } -/** - * 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 + override @property uint flags() nothrow pure const { return 1; } + + TypeInfo_Class info; +} + +class TypeInfo_Struct : TypeInfo { + override string toString() const { return name; } - /** - * 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) statement should be used for that purpose. - */ - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) + override bool opEquals(Object o) { - super(msg, file, line, nextInChain); + if (this is o) + return true; + auto s = cast(const TypeInfo_Struct)o; + return s && this.name == s.name && + this.initializer().length == s.initializer().length; } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + override size_t getHash(scope const void* p) @trusted pure nothrow const { - super(msg, file, line, nextInChain); + assert(p); + if (xtoHash) + { + return (*xtoHash)(p); + } + else + { + import core.internal.traits : externDFunc; + alias hashOf = externDFunc!("rt.util.hash.hashOf", + size_t function(const(void)[], size_t) @trusted pure nothrow @nogc); + return hashOf(p[0 .. initializer().length], 0); + } } -} -unittest -{ + override bool equals(in void* p1, in void* p2) @trusted pure nothrow const { - auto e = new Exception("msg"); - assert(e.file == __FILE__); - assert(e.line == __LINE__ - 2); - assert(e.nextInChain is null); - assert(e.msg == "msg"); + import core.stdc.string : memcmp; + + if (!p1 || !p2) + return false; + else if (xopEquals) + return (*xopEquals)(p1, 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 { - auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42); - assert(e.file == "hello"); - assert(e.line == 42); - assert(e.nextInChain !is null); - assert(e.msg == "msg"); + 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) + return (*xopCmp)(p2, p1); + 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 { - auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!")); - assert(e.file == "hello"); - assert(e.line == 42); - assert(e.nextInChain !is null); - assert(e.msg == "msg"); + return initializer().length; } + override const(void)[] initializer() nothrow pure const @safe { - auto e = new Exception("message"); - assert(e.message == "message"); + return m_init; } -} + override @property uint flags() nothrow pure const { return m_flags; } -/** - * 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) + override @property size_t talign() nothrow pure const { return m_align; } + + final override void destroy(void* p) const { - super(msg, nextInChain); - bypassedException = null; + if (xdtor) + { + if (m_flags & StructFlags.isDynamicType) + (*xdtorti)(p, this); + else + (*xdtor)(p); + } } - @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null) + override void postblit(void* p) const { - super(msg, file, line, nextInChain); - bypassedException = null; + if (xpostblit) + (*xpostblit)(p); } - /** The first $(D Exception) which was bypassed when this Error was thrown, - or $(D null) if no $(D Exception)s were pending. */ - Throwable bypassedException; -} + string name; + void[] m_init; // initializer; m_init.ptr == null if 0 initialize -unittest -{ + @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 { - auto e = new Error("msg"); - assert(e.file is null); - assert(e.line == 0); - assert(e.nextInChain is null); - assert(e.msg == "msg"); - assert(e.bypassedException is null); + hasPointers = 0x1, + isDynamicType = 0x2, // built at runtime, needs type info in xdtor } - + StructFlags m_flags; + } + union { - auto e = new Error("msg", new Exception("It's an Exception!")); - assert(e.file is null); - assert(e.line == 0); - assert(e.nextInChain !is null); - assert(e.msg == "msg"); - assert(e.bypassedException is null); + void function(void*) xdtor; + void function(void*, const TypeInfo_Struct ti) xdtorti; } + void function(void*) xpostblit; + + uint m_align; + override @property immutable(void)* rtInfo() const { return m_RTInfo; } + + version (X86_64) { - auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!")); - assert(e.file == "hello"); - assert(e.line == 42); - assert(e.nextInChain !is null); - assert(e.msg == "msg"); - assert(e.bypassedException is null); + 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 } -/* Used in Exception Handling LSDA tables to 'wrap' C++ type info - * so it can be distinguished from D TypeInfo - */ -class __cpp_type_info_ptr +unittest { - void* ptr; // opaque pointer to C++ RTTI type info + struct S + { + bool opEquals(ref const S rhs) const + { + return false; + } + } + S s; + assert(!typeid(S).equals(&s, &s)); } -extern (C) +class TypeInfo_Tuple : TypeInfo { - // from druntime/src/rt/aaA.d - - // size_t _aaLen(in void* p) pure nothrow @nogc; - private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow; - // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); - inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow; - inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow; - void* _aaRehash(void** pp, in TypeInfo keyti) pure nothrow; - void _aaClear(void* p) pure nothrow; + TypeInfo[] elements; - // alias _dg_t = extern(D) int delegate(void*); - // int _aaApply(void* aa, size_t keysize, _dg_t dg); + override string toString() const + { + string s = "("; + foreach (i, element; elements) + { + if (i) + s ~= ','; + s ~= element.toString(); + } + s ~= ")"; + return s; + } - // alias _dg2_t = extern(D) int delegate(void*, void*); - // int _aaApply2(void* aa, size_t keysize, _dg2_t dg); + override bool opEquals(Object o) + { + if (this is o) + return true; - private struct AARange { void* impl; size_t idx; } - AARange _aaRange(void* aa) pure nothrow @nogc @safe; - bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe; - void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe; - void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe; - void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe; + 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; + } - int _aaEqual(in TypeInfo tiRaw, in void* e1, in void* e2); - hash_t _aaGetHash(in void* aa, in TypeInfo tiRaw) nothrow; + override size_t getHash(scope const void* p) const + { + assert(0); + } - /* - _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code. - This is a typesystem hole, however this is existing hole. - Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus - copiler allowed to create AA literal with keys, which have impure unsafe toHash methods. - */ - void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure; -} + override bool equals(in void* p1, in void* p2) const + { + assert(0); + } -void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure -{ - return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values); -} + override int compare(in void* p1, in void* p2) const + { + assert(0); + } -alias AssociativeArray(Key, Value) = Value[Key]; + override @property size_t tsize() nothrow pure const + { + assert(0); + } -void clear(T : Value[Key], Value, Key)(T aa) -{ - _aaClear(*cast(void **) &aa); -} + override const(void)[] initializer() const @trusted + { + assert(0); + } -void clear(T : Value[Key], Value, Key)(T* aa) -{ - _aaClear(*cast(void **) aa); -} + override void swap(void* p1, void* p2) const + { + assert(0); + } -T rehash(T : Value[Key], Value, Key)(T aa) -{ - _aaRehash(cast(void**)&aa, typeid(Value[Key])); - return aa; -} + override void destroy(void* p) const + { + assert(0); + } -T rehash(T : Value[Key], Value, Key)(T* aa) -{ - _aaRehash(cast(void**)aa, typeid(Value[Key])); - return *aa; -} + override void postblit(void* p) const + { + assert(0); + } -T rehash(T : shared Value[Key], Value, Key)(T aa) -{ - _aaRehash(cast(void**)&aa, typeid(Value[Key])); - return aa; -} + override @property size_t talign() nothrow pure const + { + assert(0); + } -T rehash(T : shared Value[Key], Value, Key)(T* aa) -{ - _aaRehash(cast(void**)aa, typeid(Value[Key])); - return *aa; + version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + { + assert(0); + } } -V[K] dup(T : V[K], K, V)(T aa) +class TypeInfo_Const : TypeInfo { - //pragma(msg, "K = ", K, ", V = ", V); + override string toString() const + { + return cast(string) ("const(" ~ base.toString() ~ ")"); + } - // Bug10720 - check whether V is copyable - static assert(is(typeof({ V v = aa[K.init]; })), - "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable"); + //override bool opEquals(Object o) { return base.opEquals(o); } + override bool opEquals(Object o) + { + if (this is o) + return true; - V[K] result; + if (typeid(this) != typeid(o)) + return false; - //foreach (k, ref v; aa) - // result[k] = v; // Bug13701 - won't work if V is not mutable + auto t = cast(TypeInfo_Const)o; + return base.opEquals(t.base); + } - ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow - { - import core.stdc.string : memcpy; + 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); } - void* pv = _aaGetY(cast(void**)&result, typeid(V[K]), V.sizeof, &k); - memcpy(pv, &v, V.sizeof); - return *cast(V*)pv; - } + override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } + override @property uint flags() nothrow pure const { return base.flags; } - if (auto postblit = _getPostblit!V()) + override const(void)[] initializer() nothrow pure const { - foreach (k, ref v; aa) - postblit(duplicateElem(k, v)); + return base.initializer(); } - else + + override @property size_t talign() nothrow pure const { return base.talign; } + + version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) { - foreach (k, ref v; aa) - duplicateElem(k, v); + return base.argTypes(arg1, arg2); } - return result; + TypeInfo base; } -V[K] dup(T : V[K], K, V)(T* aa) +class TypeInfo_Invariant : TypeInfo_Const { - return (*aa).dup; + override string toString() const + { + return cast(string) ("immutable(" ~ base.toString() ~ ")"); + } } -// this should never be made public. -private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe +class TypeInfo_Shared : TypeInfo_Const { - // ensure we are dealing with a genuine AA. - static if (is(const(V[K]) == const(T))) - alias realAA = aa; - else - const(V[K]) realAA = aa; - return _aaRange(() @trusted { return cast(void*)realAA; } ()); + override string toString() const + { + return cast(string) ("shared(" ~ base.toString() ~ ")"); + } } -auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe +class TypeInfo_Inout : TypeInfo_Const { - import core.internal.traits : substInout; - - static struct Result + override string toString() const { - AARange r; - - pure nothrow @nogc: - @property bool empty() @safe { return _aaRangeEmpty(r); } - @property ref front() - { - auto p = (() @trusted => cast(substInout!K*) _aaRangeFrontKey(r)) (); - return *p; - } - void popFront() @safe { _aaRangePopFront(r); } - @property Result save() { return this; } + return cast(string) ("inout(" ~ base.toString() ~ ")"); } - - return Result(_aaToRange(aa)); } -auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc +// Contents of Moduleinfo._flags +enum { - return (*aa).byKey(); + 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, } -auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe +/***************************************** + * 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 { - import core.internal.traits : substInout; + uint _flags; // MIxxxx + uint _index; // index into _moduleinfo_array[] - static struct Result + version (all) { - AARange r; + deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.") + void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; } + } + else + { + @disable this(); + } - pure nothrow @nogc: - @property bool empty() @safe { return _aaRangeEmpty(r); } - @property ref front() +const: + private void* addrOf(int flag) 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) { - auto p = (() @trusted => cast(substInout!V*) _aaRangeFrontValue(r)) (); - return *p; + if (flag == MItlsctor) return p; + p += typeof(tlsctor).sizeof; } - void popFront() @safe { _aaRangePopFront(r); } - @property Result save() { return this; } + 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); } - return Result(_aaToRange(aa)); -} + @property uint index() nothrow pure @nogc { return _index; } -auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc -{ - return (*aa).byValue(); -} + @property uint flags() nothrow pure @nogc { return _flags; } -auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe -{ - import core.internal.traits : substInout; + /************************ + * 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; + } - static struct Result + /************************ + * Returns: + * module destructor for thread locals, `null` if there isn't one + */ + @property void function() tlsdtor() nothrow pure @nogc { - AARange r; + return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null; + } - pure nothrow @nogc: - @property bool empty() @safe { return _aaRangeEmpty(r); } - @property auto front() + /***************************** + * 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() nothrow pure @nogc + { + if (flags & MIimportedModules) { - static struct Pair - { - // We save the pointers here so that the Pair we return - // won't mutate when Result.popFront is called afterwards. - private void* keyp; - private void* valp; + auto p = cast(size_t*)addrOf(MIimportedModules); + return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p]; + } + return null; + } - @property ref key() inout - { - auto p = (() @trusted => cast(substInout!K*) keyp) (); - return *p; - }; - @property ref value() inout - { - auto p = (() @trusted => cast(substInout!V*) valp) (); - return *p; - }; - } - return Pair(_aaRangeFrontKey(r), - _aaRangeFrontValue(r)); + /**************** + * Returns: + * array of TypeInfo_Class references for classes defined in this module + */ + @property TypeInfo_Class[] localClasses() nothrow pure @nogc + { + if (flags & MIlocalClasses) + { + auto p = cast(size_t*)addrOf(MIlocalClasses); + return (cast(TypeInfo_Class*)(p + 1))[0 .. *p]; } - void popFront() @safe { return _aaRangePopFront(r); } - @property Result save() { return this; } + return null; } - return Result(_aaToRange(aa)); -} + /******************** + * Returns: + * name of module, `null` if no name + */ + @property string name() nothrow pure @nogc + { + if (true || flags & MIname) // always available for now + { + import core.stdc.string : strlen; -auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc -{ - return (*aa).byKeyValue(); -} + auto p = cast(immutable char*)addrOf(MIname); + return p[0 .. strlen(p)]; + } + // return null; + } -Key[] keys(T : Value[Key], Value, Key)(T aa) @property -{ - auto a = cast(void[])_aaKeys(cast(inout(void)*)aa, Key.sizeof, typeid(Key[])); - auto res = *cast(Key[]*)&a; - _doPostblit(res); - return res; + 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)); + } } -Key[] keys(T : Value[Key], Value, Key)(T *aa) @property +unittest { - return (*aa).keys; + ModuleInfo* m1; + foreach (m; ModuleInfo) + { + m1 = m; + } } -Value[] values(T : Value[Key], Value, Key)(T aa) @property -{ - auto a = cast(void[])_aaValues(cast(inout(void)*)aa, Key.sizeof, Value.sizeof, typeid(Value[])); - auto res = *cast(Value[]*)&a; - _doPostblit(res); - return res; -} +/////////////////////////////////////////////////////////////////////////////// +// Throwable +/////////////////////////////////////////////////////////////////////////////// -Value[] values(T : Value[Key], Value, Key)(T *aa) @property -{ - return (*aa).values; -} -unittest +/** + * 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 { - static struct T + interface TraceInfo { - byte b; - static size_t count; - this(this) { ++count; } + int opApply(scope int delegate(ref const(char[]))) const; + int opApply(scope int delegate(ref size_t, ref const(char[]))) const; + string toString() const; } - T[int] aa; - T t; - aa[0] = t; - aa[1] = t; - assert(T.count == 2); - auto vals = aa.values; - assert(vals.length == 2); - assert(T.count == 4); - T.count = 0; - int[T] aa2; - aa2[t] = 0; - assert(T.count == 1); - aa2[t] = 1; - assert(T.count == 1); - auto keys = aa2.keys; - assert(keys.length == 1); - assert(T.count == 2); -} + string msg; /// A message describing the error. -inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) -{ - auto p = key in aa; - return p ? *p : defaultValue; -} + /** + * 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; -inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) -{ - return (*aa).get(key, defaultValue); -} + /** + * 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; + + /** + * 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. + */ + private Throwable nextInChain; + + 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() @safe inout return scope pure nothrow @nogc { return nextInChain; } + + /** + * Replace next in chain with `tail`. + * Use `chainTogether` instead if at all possible. + */ + @property void next(Throwable tail) @safe scope pure nothrow @nogc + { + if (tail && tail._refcount) + ++tail._refcount; // increment the replacement *first* + + auto n = nextInChain; + nextInChain = null; // sever the tail before deleting it + + if (n && n._refcount) + _d_delThrowable(n); // now delete the old tail + + nextInChain = tail; // 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 + */ + @system @nogc final pure nothrow ref uint refcount() return scope { 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.nextInChain) + { + 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 (e2 && e2.refcount()) + ++e2.refcount(); + if (!e1) + return e2; + if (!e2) + return e1; + for (auto e = e1; 1; e = e.nextInChain) + { + if (!e.nextInChain) + { + e.nextInChain = e2; + break; + } + } + return e1; + } + + @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null) + { + this.msg = msg; + this.nextInChain = 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 (nextInChain && nextInChain._refcount) + _d_delThrowable(nextInChain); + } -@safe unittest -{ - int[string] aa; - int a; - foreach (val; aa.byKeyValue) + /** + * 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() { - ++aa[val.key]; - a = val.value; + string s; + toString((buf) { s ~= buf; }); + return s; } -} -unittest -{ - static assert(!__traits(compiles, - () @safe { - struct BadValue + /** + * 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, 10)); sink(")"); + + if (msg.length) + { + sink(": "); sink(msg); + } + if (info) + { + try { - int x; - this(this) @safe { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe - alias x this; + sink("\n----------------"); + foreach (t; info) + { + sink("\n"); sink(t); + } + } + catch (Throwable) + { + // ignore more errors } - - BadValue[int] aa; - () @safe { auto x = aa.byKey.front; } (); } - )); -} - -pure nothrow unittest -{ - int[int] a; - foreach (i; a.byKey) - { - assert(false); } - foreach (i; a.byValue) + + /** + * Get the message describing the error. + * Base behavior is to return the `Throwable.msg` field. + * Override to return some other error message. + * + * Returns: + * Error message + */ + @__future const(char)[] message() const { - assert(false); + return this.msg; } } -pure /*nothrow */ unittest + +/** + * 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 { - auto a = [ 1:"one", 2:"two", 3:"three" ]; - auto b = a.dup; - assert(b == [ 1:"one", 2:"two", 3:"three" ]); - int[] c; - foreach (k; a.byKey) + /** + * 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) statement should be used for that purpose. + */ + @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) { - c ~= k; + super(msg, file, line, nextInChain); } - assert(c.length == 3); - assert(c[0] == 1 || c[1] == 1 || c[2] == 1); - assert(c[0] == 2 || c[1] == 2 || c[2] == 2); - assert(c[0] == 3 || c[1] == 3 || c[2] == 3); + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + { + super(msg, file, line, nextInChain); + } } -pure nothrow unittest +unittest { - // test for bug 5925 - const a = [4:0]; - const b = [4:0]; - assert(a == b); -} + { + auto e = new Exception("msg"); + assert(e.file == __FILE__); + assert(e.line == __LINE__ - 2); + assert(e.nextInChain is null); + assert(e.msg == "msg"); + } -pure nothrow unittest -{ - // test for bug 9052 - static struct Json { - Json[string] aa; - void opAssign(Json) {} - size_t length() const { return aa.length; } - // This length() instantiates AssociativeArray!(string, const(Json)) to call AA.length(), and - // inside ref Slot opAssign(Slot p); (which is automatically generated by compiler in Slot), - // this.value = p.value would actually fail, because both side types of the assignment - // are const(Json). + { + auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42); + assert(e.file == "hello"); + assert(e.line == 42); + assert(e.nextInChain !is null); + assert(e.msg == "msg"); } -} -pure nothrow unittest -{ - // test for bug 8583: ensure Slot and aaA are on the same page wrt value alignment - string[byte] aa0 = [0: "zero"]; - string[uint[3]] aa1 = [[1,2,3]: "onetwothree"]; - ushort[uint[3]] aa2 = [[9,8,7]: 987]; - ushort[uint[4]] aa3 = [[1,2,3,4]: 1234]; - string[uint[5]] aa4 = [[1,2,3,4,5]: "onetwothreefourfive"]; + { + auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!")); + assert(e.file == "hello"); + assert(e.line == 42); + assert(e.nextInChain !is null); + assert(e.msg == "msg"); + } - assert(aa0.byValue.front == "zero"); - assert(aa1.byValue.front == "onetwothree"); - assert(aa2.byValue.front == 987); - assert(aa3.byValue.front == 1234); - assert(aa4.byValue.front == "onetwothreefourfive"); + { + auto e = new Exception("message"); + assert(e.message == "message"); + } } -pure nothrow unittest + +/** + * 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 { - // test for bug 10720 - static struct NC + /** + * 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) { - @disable this(this) { } + super(msg, nextInChain); + bypassedException = null; } - NC[string] aa; - static assert(!is(aa.nonExistingField)); -} + @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null) + { + super(msg, file, line, nextInChain); + bypassedException = null; + } -pure nothrow unittest -{ - // bug 5842 - string[string] test = null; - test["test1"] = "test1"; - test.remove("test1"); - test.rehash; - test["test3"] = "test3"; // causes divide by zero if rehash broke the AA + /** The first $(D Exception) which was bypassed when this Error was thrown, + or $(D null) if no $(D Exception)s were pending. */ + Throwable bypassedException; } -pure nothrow unittest +unittest { - string[] keys = ["a", "b", "c", "d", "e", "f"]; - - // Test forward range capabilities of byKey { - int[string] aa; - foreach (key; keys) - aa[key] = 0; - - auto keyRange = aa.byKey(); - auto savedKeyRange = keyRange.save; - - // Consume key range once - size_t keyCount = 0; - while (!keyRange.empty) - { - aa[keyRange.front]++; - keyCount++; - keyRange.popFront(); - } - - foreach (key; keys) - { - assert(aa[key] == 1); - } - assert(keyCount == keys.length); - - // Verify it's possible to iterate the range the second time - keyCount = 0; - while (!savedKeyRange.empty) - { - aa[savedKeyRange.front]++; - keyCount++; - savedKeyRange.popFront(); - } + auto e = new Error("msg"); + assert(e.file is null); + assert(e.line == 0); + assert(e.nextInChain is null); + assert(e.msg == "msg"); + assert(e.bypassedException is null); + } - foreach (key; keys) - { - assert(aa[key] == 2); - } - assert(keyCount == keys.length); + { + auto e = new Error("msg", new Exception("It's an Exception!")); + assert(e.file is null); + assert(e.line == 0); + assert(e.nextInChain !is null); + assert(e.msg == "msg"); + assert(e.bypassedException is null); } - // Test forward range capabilities of byValue { - size_t[string] aa; - foreach (i; 0 .. keys.length) - { - aa[keys[i]] = i; - } + auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!")); + assert(e.file == "hello"); + assert(e.line == 42); + assert(e.nextInChain !is null); + assert(e.msg == "msg"); + assert(e.bypassedException is null); + } +} - auto valRange = aa.byValue(); - auto savedValRange = valRange.save; +/* 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 +} - // Consume value range once - int[] hasSeen; - hasSeen.length = keys.length; - while (!valRange.empty) - { - assert(hasSeen[valRange.front] == 0); - hasSeen[valRange.front]++; - valRange.popFront(); - } +extern (C) +{ + // from druntime/src/rt/aaA.d - foreach (sawValue; hasSeen) { assert(sawValue == 1); } + // size_t _aaLen(in void* p) pure nothrow @nogc; + private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow; + // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); + inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow; + inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow; + void* _aaRehash(void** pp, in TypeInfo keyti) pure nothrow; + void _aaClear(void* p) pure nothrow; - // Verify it's possible to iterate the range the second time - hasSeen = null; - hasSeen.length = keys.length; - while (!savedValRange.empty) - { - assert(!hasSeen[savedValRange.front]); - hasSeen[savedValRange.front] = true; - savedValRange.popFront(); - } + // alias _dg_t = extern(D) int delegate(void*); + // int _aaApply(void* aa, size_t keysize, _dg_t dg); - foreach (sawValue; hasSeen) { assert(sawValue); } - } + // alias _dg2_t = extern(D) int delegate(void*, void*); + // int _aaApply2(void* aa, size_t keysize, _dg2_t dg); + + private struct AARange { void* impl; size_t idx; } + AARange _aaRange(void* aa) pure nothrow @nogc @safe; + bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe; + void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe; + void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe; + void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe; + + int _aaEqual(in TypeInfo tiRaw, in void* e1, in void* e2); + hash_t _aaGetHash(in void* aa, in TypeInfo tiRaw) nothrow; + + /* + _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code. + This is a typesystem hole, however this is existing hole. + Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus + copiler allowed to create AA literal with keys, which have impure unsafe toHash methods. + */ + void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure; } -pure nothrow unittest +void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure { - // expanded test for 5842: increase AA size past the point where the AA - // stops using binit, in order to test another code path in rehash. - int[int] aa; - foreach (int i; 0 .. 32) - aa[i] = i; - foreach (int i; 0 .. 32) - aa.remove(i); - aa.rehash; - aa[1] = 1; + return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values); } -pure nothrow unittest +alias AssociativeArray(Key, Value) = Value[Key]; + +void clear(T : Value[Key], Value, Key)(T aa) { - // bug 13078 - shared string[][string] map; - map.rehash; + _aaClear(*cast(void **) &aa); } -pure nothrow unittest +void clear(T : Value[Key], Value, Key)(T* aa) { - // bug 11761: test forward range functionality - auto aa = ["a": 1]; + _aaClear(*cast(void **) aa); +} - void testFwdRange(R, T)(R fwdRange, T testValue) - { - assert(!fwdRange.empty); - assert(fwdRange.front == testValue); - static assert(is(typeof(fwdRange.save) == typeof(fwdRange))); +T rehash(T : Value[Key], Value, Key)(T aa) +{ + _aaRehash(cast(void**)&aa, typeid(Value[Key])); + return aa; +} - auto saved = fwdRange.save; - fwdRange.popFront(); - assert(fwdRange.empty); +T rehash(T : Value[Key], Value, Key)(T* aa) +{ + _aaRehash(cast(void**)aa, typeid(Value[Key])); + return *aa; +} - assert(!saved.empty); - assert(saved.front == testValue); - saved.popFront(); - assert(saved.empty); - } +T rehash(T : shared Value[Key], Value, Key)(T aa) +{ + _aaRehash(cast(void**)&aa, typeid(Value[Key])); + return aa; +} - testFwdRange(aa.byKey, "a"); - testFwdRange(aa.byValue, 1); - //testFwdRange(aa.byPair, tuple("a", 1)); +T rehash(T : shared Value[Key], Value, Key)(T* aa) +{ + _aaRehash(cast(void**)aa, typeid(Value[Key])); + return *aa; } -unittest +V[K] dup(T : V[K], K, V)(T aa) { - // Issue 9119 - int[string] aa; - assert(aa.byKeyValue.empty); + //pragma(msg, "K = ", K, ", V = ", V); - aa["a"] = 1; - aa["b"] = 2; - aa["c"] = 3; + // Bug10720 - check whether V is copyable + static assert(is(typeof({ V v = aa[K.init]; })), + "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable"); - auto pairs = aa.byKeyValue; + V[K] result; - auto savedPairs = pairs.save; - size_t count = 0; - while (!pairs.empty) + //foreach (k, ref v; aa) + // result[k] = v; // Bug13701 - won't work if V is not mutable + + ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow { - assert(pairs.front.key in aa); - assert(pairs.front.value == aa[pairs.front.key]); - count++; - pairs.popFront(); + import core.stdc.string : memcpy; + + void* pv = _aaGetY(cast(void**)&result, typeid(V[K]), V.sizeof, &k); + memcpy(pv, &v, V.sizeof); + return *cast(V*)pv; } - assert(count == aa.length); - // Verify that saved range can iterate over the AA again - count = 0; - while (!savedPairs.empty) + if (auto postblit = _getPostblit!V()) { - assert(savedPairs.front.key in aa); - assert(savedPairs.front.value == aa[savedPairs.front.key]); - count++; - savedPairs.popFront(); + foreach (k, ref v; aa) + postblit(duplicateElem(k, v)); } - assert(count == aa.length); + else + { + foreach (k, ref v; aa) + duplicateElem(k, v); + } + + return result; } -unittest +V[K] dup(T : V[K], K, V)(T* aa) { - // Verify iteration with const. - auto aa = [1:2, 3:4]; - foreach (const t; aa.byKeyValue) - { - auto k = t.key; - auto v = t.value; - } + return (*aa).dup; } -unittest +// this should never be made public. +private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe { - // test for bug 14626 - static struct S - { - string[string] aa; - inout(string) key() inout { return aa.byKey().front; } - inout(string) val() inout { return aa.byValue().front; } - auto keyval() inout { return aa.byKeyValue().front; } - } + // ensure we are dealing with a genuine AA. + static if (is(const(V[K]) == const(T))) + alias realAA = aa; + else + const(V[K]) realAA = aa; + return _aaRange(() @trusted { return cast(void*)realAA; } ()); +} - S s = S(["a":"b"]); - assert(s.key() == "a"); - assert(s.val() == "b"); - assert(s.keyval().key == "a"); - assert(s.keyval().value == "b"); +auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe +{ + import core.internal.traits : substInout; - void testInoutKeyVal(inout(string) key) + static struct Result { - inout(string)[typeof(key)] aa; + AARange r; - foreach (i; aa.byKey()) {} - foreach (i; aa.byValue()) {} - foreach (i; aa.byKeyValue()) {} + pure nothrow @nogc: + @property bool empty() @safe { return _aaRangeEmpty(r); } + @property ref front() + { + auto p = (() @trusted => cast(substInout!K*) _aaRangeFrontKey(r)) (); + return *p; + } + void popFront() @safe { _aaRangePopFront(r); } + @property Result save() { return this; } } - - const int[int] caa; - static assert(is(typeof(caa.byValue().front) == const int)); + + return Result(_aaToRange(aa)); } -private void _destructRecurse(S)(ref S s) - if (is(S == struct)) +auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc { - static if (__traits(hasMember, S, "__xdtor") && - // Bugzilla 14746: Check that it's the exact member of S. - __traits(isSame, S, __traits(parent, s.__xdtor))) - s.__xdtor(); + return (*aa).byKey(); } -private void _destructRecurse(E, size_t n)(ref E[n] arr) +auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { - import core.internal.traits : hasElaborateDestructor; + import core.internal.traits : substInout; - static if (hasElaborateDestructor!E) + static struct Result { - foreach_reverse (ref elem; arr) - _destructRecurse(elem); + AARange r; + + pure nothrow @nogc: + @property bool empty() @safe { return _aaRangeEmpty(r); } + @property ref front() + { + auto p = (() @trusted => cast(substInout!V*) _aaRangeFrontValue(r)) (); + return *p; + } + void popFront() @safe { _aaRangePopFront(r); } + @property Result save() { return this; } } + + return Result(_aaToRange(aa)); } -// Public and explicitly undocumented -void _postblitRecurse(S)(ref S s) - if (is(S == struct)) +auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc { - static if (__traits(hasMember, S, "__xpostblit") && - // Bugzilla 14746: Check that it's the exact member of S. - __traits(isSame, S, __traits(parent, s.__xpostblit))) - s.__xpostblit(); + return (*aa).byValue(); } -// Ditto -void _postblitRecurse(E, size_t n)(ref E[n] arr) +auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { - import core.internal.traits : hasElaborateCopyConstructor; + import core.internal.traits : substInout; - static if (hasElaborateCopyConstructor!E) + static struct Result { - size_t i; - scope(failure) + AARange r; + + pure nothrow @nogc: + @property bool empty() @safe { return _aaRangeEmpty(r); } + @property auto front() { - for (; i != 0; --i) + static struct Pair { - _destructRecurse(arr[i - 1]); // What to do if this throws? + // We save the pointers here so that the Pair we return + // won't mutate when Result.popFront is called afterwards. + private void* keyp; + private void* valp; + + @property ref key() inout + { + auto p = (() @trusted => cast(substInout!K*) keyp) (); + return *p; + }; + @property ref value() inout + { + auto p = (() @trusted => cast(substInout!V*) valp) (); + return *p; + }; } + return Pair(_aaRangeFrontKey(r), + _aaRangeFrontValue(r)); } - - for (i = 0; i < arr.length; ++i) - _postblitRecurse(arr[i]); + void popFront() @safe { return _aaRangePopFront(r); } + @property Result save() { return this; } } + + return Result(_aaToRange(aa)); } -// Test destruction/postblit order -@safe nothrow pure unittest +auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc { - string[] order; + return (*aa).byKeyValue(); +} - struct InnerTop - { - ~this() @safe nothrow pure - { - order ~= "destroy inner top"; - } +Key[] keys(T : Value[Key], Value, Key)(T aa) @property +{ + auto a = cast(void[])_aaKeys(cast(inout(void)*)aa, Key.sizeof, typeid(Key[])); + auto res = *cast(Key[]*)&a; + _doPostblit(res); + return res; +} - this(this) @safe nothrow pure - { - order ~= "copy inner top"; - } - } +Key[] keys(T : Value[Key], Value, Key)(T *aa) @property +{ + return (*aa).keys; +} - struct InnerMiddle {} +Value[] values(T : Value[Key], Value, Key)(T aa) @property +{ + auto a = cast(void[])_aaValues(cast(inout(void)*)aa, Key.sizeof, Value.sizeof, typeid(Value[])); + auto res = *cast(Value[]*)&a; + _doPostblit(res); + return res; +} - version(none) // https://issues.dlang.org/show_bug.cgi?id=14242 - struct InnerElement +Value[] values(T : Value[Key], Value, Key)(T *aa) @property +{ + return (*aa).values; +} + +unittest +{ + static struct T { - static char counter = '1'; + byte b; + static size_t count; + this(this) { ++count; } + } + T[int] aa; + T t; + aa[0] = t; + aa[1] = t; + assert(T.count == 2); + auto vals = aa.values; + assert(vals.length == 2); + assert(T.count == 4); - ~this() @safe nothrow pure - { - order ~= "destroy inner element #" ~ counter++; - } + T.count = 0; + int[T] aa2; + aa2[t] = 0; + assert(T.count == 1); + aa2[t] = 1; + assert(T.count == 1); + auto keys = aa2.keys; + assert(keys.length == 1); + assert(T.count == 2); +} - this(this) @safe nothrow pure - { - order ~= "copy inner element #" ~ counter++; - } - } +inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) +{ + auto p = key in aa; + return p ? *p : defaultValue; +} - struct InnerBottom +inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) +{ + return (*aa).get(key, defaultValue); +} + +@safe unittest +{ + int[string] aa; + int a; + foreach (val; aa.byKeyValue) { - ~this() @safe nothrow pure - { - order ~= "destroy inner bottom"; - } + ++aa[val.key]; + a = val.value; + } +} - this(this) @safe nothrow pure - { - order ~= "copy inner bottom"; +unittest +{ + static assert(!__traits(compiles, + () @safe { + struct BadValue + { + int x; + this(this) @safe { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe + alias x this; + } + + BadValue[int] aa; + () @safe { auto x = aa.byKey.front; } (); } - } + )); +} - struct S +pure nothrow unittest +{ + int[int] a; + foreach (i; a.byKey) { - char[] s; - InnerTop top; - InnerMiddle middle; - version(none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242 - int a; - InnerBottom bottom; - ~this() @safe nothrow pure { order ~= "destroy outer"; } - this(this) @safe nothrow pure { order ~= "copy outer"; } + assert(false); + } + foreach (i; a.byValue) + { + assert(false); } +} - string[] destructRecurseOrder; +pure /*nothrow */ unittest +{ + auto a = [ 1:"one", 2:"two", 3:"three" ]; + auto b = a.dup; + assert(b == [ 1:"one", 2:"two", 3:"three" ]); + + int[] c; + foreach (k; a.byKey) { - S s; - _destructRecurse(s); - destructRecurseOrder = order; - order = null; + c ~= k; } - assert(order.length); - assert(destructRecurseOrder == order); - order = null; + assert(c.length == 3); + assert(c[0] == 1 || c[1] == 1 || c[2] == 1); + assert(c[0] == 2 || c[1] == 2 || c[2] == 2); + assert(c[0] == 3 || c[1] == 3 || c[2] == 3); +} - S s; - _postblitRecurse(s); - assert(order.length); - auto postblitRecurseOrder = order; - order = null; - S s2 = s; - assert(order.length); - assert(postblitRecurseOrder == order); +pure nothrow unittest +{ + // test for bug 5925 + const a = [4:0]; + const b = [4:0]; + assert(a == b); } -// Test static struct -nothrow @safe @nogc unittest +pure nothrow unittest { - static int i = 0; - static struct S { ~this() nothrow @safe @nogc { i = 42; } } - S s; - _destructRecurse(s); - assert(i == 42); + // test for bug 9052 + static struct Json { + Json[string] aa; + void opAssign(Json) {} + size_t length() const { return aa.length; } + // This length() instantiates AssociativeArray!(string, const(Json)) to call AA.length(), and + // inside ref Slot opAssign(Slot p); (which is automatically generated by compiler in Slot), + // this.value = p.value would actually fail, because both side types of the assignment + // are const(Json). + } } -unittest +pure nothrow unittest { - // Bugzilla 14746 - static struct HasDtor - { - ~this() { assert(0); } - } - static struct Owner - { - HasDtor* ptr; - alias ptr this; - } + // test for bug 8583: ensure Slot and aaA are on the same page wrt value alignment + string[byte] aa0 = [0: "zero"]; + string[uint[3]] aa1 = [[1,2,3]: "onetwothree"]; + ushort[uint[3]] aa2 = [[9,8,7]: 987]; + ushort[uint[4]] aa3 = [[1,2,3,4]: 1234]; + string[uint[5]] aa4 = [[1,2,3,4,5]: "onetwothreefourfive"]; - Owner o; - assert(o.ptr is null); - destroy(o); // must not reach in HasDtor.__dtor() + assert(aa0.byValue.front == "zero"); + assert(aa1.byValue.front == "onetwothree"); + assert(aa2.byValue.front == 987); + assert(aa3.byValue.front == 1234); + assert(aa4.byValue.front == "onetwothreefourfive"); } -unittest +pure nothrow unittest { - // Bugzilla 14746 - static struct HasPostblit - { - this(this) { assert(0); } - } - static struct Owner + // test for bug 10720 + static struct NC { - HasPostblit* ptr; - alias ptr this; + @disable this(this) { } } - Owner o; - assert(o.ptr is null); - _postblitRecurse(o); // must not reach in HasPostblit.__postblit() + NC[string] aa; + static assert(!is(aa.nonExistingField)); } -// Test handling of fixed-length arrays -// Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242 -unittest +pure nothrow unittest { - string[] order; + // bug 5842 + string[string] test = null; + test["test1"] = "test1"; + test.remove("test1"); + test.rehash; + test["test3"] = "test3"; // causes divide by zero if rehash broke the AA +} - struct S +pure nothrow unittest +{ + string[] keys = ["a", "b", "c", "d", "e", "f"]; + + // Test forward range capabilities of byKey { - char id; + int[string] aa; + foreach (key; keys) + aa[key] = 0; - this(this) + auto keyRange = aa.byKey(); + auto savedKeyRange = keyRange.save; + + // Consume key range once + size_t keyCount = 0; + while (!keyRange.empty) { - order ~= "copy #" ~ id; + aa[keyRange.front]++; + keyCount++; + keyRange.popFront(); } - ~this() + foreach (key; keys) { - order ~= "destroy #" ~ id; + assert(aa[key] == 1); } - } - - string[] destructRecurseOrder; - { - S[3] arr = [S('1'), S('2'), S('3')]; - _destructRecurse(arr); - destructRecurseOrder = order; - order = null; - } - assert(order.length); - assert(destructRecurseOrder == order); - order = null; - - S[3] arr = [S('1'), S('2'), S('3')]; - _postblitRecurse(arr); - assert(order.length); - auto postblitRecurseOrder = order; - order = null; - - auto arrCopy = arr; - assert(order.length); - assert(postblitRecurseOrder == order); -} - -// Test handling of failed postblit -// Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242 -/+ nothrow @safe +/ unittest -{ - static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } } - static string[] order; - static struct Inner - { - char id; + assert(keyCount == keys.length); - @safe: - this(this) + // Verify it's possible to iterate the range the second time + keyCount = 0; + while (!savedKeyRange.empty) { - order ~= "copy inner #" ~ id; - if(id == '2') - throw new FailedPostblitException(); + aa[savedKeyRange.front]++; + keyCount++; + savedKeyRange.popFront(); } - ~this() nothrow + foreach (key; keys) { - order ~= "destroy inner #" ~ id; + assert(aa[key] == 2); } + assert(keyCount == keys.length); } - static struct Outer + // Test forward range capabilities of byValue { - Inner inner1, inner2, inner3; - - nothrow @safe: - this(char first, char second, char third) + size_t[string] aa; + foreach (i; 0 .. keys.length) { - inner1 = Inner(first); - inner2 = Inner(second); - inner3 = Inner(third); + aa[keys[i]] = i; } - this(this) - { - order ~= "copy outer"; - } + auto valRange = aa.byValue(); + auto savedValRange = valRange.save; - ~this() + // Consume value range once + int[] hasSeen; + hasSeen.length = keys.length; + while (!valRange.empty) { - order ~= "destroy outer"; + assert(hasSeen[valRange.front] == 0); + hasSeen[valRange.front]++; + valRange.popFront(); } - } - - auto outer = Outer('1', '2', '3'); - - try _postblitRecurse(outer); - catch(FailedPostblitException) {} - catch(Exception) assert(false); - - auto postblitRecurseOrder = order; - order = null; - - try auto copy = outer; - catch(FailedPostblitException) {} - catch(Exception) assert(false); - - assert(postblitRecurseOrder == order); - order = null; - - Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')]; - - try _postblitRecurse(arr); - catch(FailedPostblitException) {} - catch(Exception) assert(false); - postblitRecurseOrder = order; - order = null; + foreach (sawValue; hasSeen) { assert(sawValue == 1); } - try auto arrCopy = arr; - catch(FailedPostblitException) {} - catch(Exception) assert(false); + // Verify it's possible to iterate the range the second time + hasSeen = null; + hasSeen.length = keys.length; + while (!savedValRange.empty) + { + assert(!hasSeen[savedValRange.front]); + hasSeen[savedValRange.front] = true; + savedValRange.popFront(); + } - assert(postblitRecurseOrder == order); + foreach (sawValue; hasSeen) { assert(sawValue); } + } } -/******** -Destroys the given object and sets it back to its 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. -*/ -void destroy(T)(T obj) if (is(T == class)) +pure nothrow unittest { - static if(__traits(getLinkage, T) == "C++") - { - obj.__xdtor(); - - enum classSize = __traits(classInstanceSize, T); - (cast(void*)obj)[0 .. classSize] = typeid(T).initializer[]; - } - else - rt_finalize(cast(void*)obj); + // expanded test for 5842: increase AA size past the point where the AA + // stops using binit, in order to test another code path in rehash. + int[int] aa; + foreach (int i; 0 .. 32) + aa[i] = i; + foreach (int i; 0 .. 32) + aa.remove(i); + aa.rehash; + aa[1] = 1; } -/// ditto -void destroy(T)(T obj) if (is(T == interface)) +pure nothrow unittest { - destroy(cast(Object)obj); + // bug 13078 + shared string[][string] map; + map.rehash; } -/// Reference type demonstration -unittest +pure nothrow 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` + // bug 11761: test forward range functionality + auto aa = ["a": 1]; - // check C++ classes work too! - extern (C++) class CPP + void testFwdRange(R, T)(R fwdRange, T testValue) { - struct Agg - { - __gshared int dtorCount; - - int x = 10; - ~this() { dtorCount++; } - } + assert(!fwdRange.empty); + assert(fwdRange.front == testValue); + static assert(is(typeof(fwdRange.save) == typeof(fwdRange))); - __gshared int dtorCount; + auto saved = fwdRange.save; + fwdRange.popFront(); + assert(fwdRange.empty); - string s = "S"; - Agg a; - ~this() { dtorCount++; } + assert(!saved.empty); + assert(saved.front == testValue); + saved.popFront(); + assert(saved.empty); } - 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(cpp); - assert(cpp.dtorCount == 1); // `cpp`'s destructor was called - assert(cpp.s == "S"); // `cpp.s` is back to its inital state, `"S"` - assert(cpp.a.dtorCount == 1); // `cpp.a`'s destructor was called - assert(cpp.a.x == 10); // `cpp.a.x` is back to its inital state, `10` -} - -/// Value type demonstration -unittest -{ - int i; - assert(i == 0); // `i`'s initial state is `0` - i = 1; - assert(i == 1); // `i` changed to `1` - destroy(i); - assert(i == 0); // `i` is back to its initial state `0` + testFwdRange(aa.byKey, "a"); + testFwdRange(aa.byValue, 1); + //testFwdRange(aa.byPair, tuple("a", 1)); } 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"); - } -} - -/// ditto -void destroy(T)(ref T obj) if (is(T == struct)) -{ - // We need to re-initialize `obj`. Previously, the code - // `auto init = cast(ubyte[])typeid(T).initializer()` was used, but - // `typeid` is a runtime call and requires the `TypeInfo` object which is - // not usable when compiling with -betterC. If we do `obj = T.init` then we - // end up needlessly calling postblits and destructors. So, we create a - // static immutable lvalue that can be re-used with subsequent calls to `destroy` - shared static immutable T init = T.init; + // Issue 9119 + int[string] aa; + assert(aa.byKeyValue.empty); - _destructRecurse(obj); - () @trusted { - auto dest = (cast(ubyte*) &obj)[0 .. T.sizeof]; - auto src = (cast(ubyte*) &init)[0 .. T.sizeof]; - dest[] = src[]; - } (); -} + aa["a"] = 1; + aa["b"] = 2; + aa["c"] = 3; -nothrow @safe @nogc unittest -{ - { - struct A { string s = "A"; } - A a; - 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(a); - assert(destroyed == 2); - assert(a.s == "B"); - assert(a.c.s == "C" ); - } -} + auto pairs = aa.byKeyValue; -/// ditto -void destroy(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct)) -{ - foreach_reverse (ref e; obj[]) - destroy(e); -} + auto savedPairs = pairs.save; + size_t count = 0; + while (!pairs.empty) + { + assert(pairs.front.key in aa); + assert(pairs.front.value == aa[pairs.front.key]); + count++; + pairs.popFront(); + } + assert(count == aa.length); -unittest -{ - int[2] a; - a[0] = 1; - a[1] = 2; - destroy(a); - assert(a == [ 0, 0 ]); + // Verify that saved range can iterate over the AA again + count = 0; + while (!savedPairs.empty) + { + assert(savedPairs.front.key in aa); + assert(savedPairs.front.value == aa[savedPairs.front.key]); + count++; + savedPairs.popFront(); + } + assert(count == aa.length); } unittest { - static struct vec2f { - float[2] values; - alias values this; + // Verify iteration with const. + auto aa = [1:2, 3:4]; + foreach (const t; aa.byKeyValue) + { + auto k = t.key; + auto v = t.value; } - - vec2f v; - destroy!vec2f(v); } unittest { - // Bugzilla 15009 - static string op; + // test for bug 14626 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); } + string[string] aa; + inout(string) key() inout { return aa.byKey().front; } + inout(string) val() inout { return aa.byValue().front; } + auto keyval() inout { return aa.byKeyValue().front; } } - { - 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 s = S(["a":"b"]); + assert(s.key() == "a"); + assert(s.val() == "b"); + assert(s.keyval().key == "a"); + assert(s.keyval().value == "b"); + void testInoutKeyVal(inout(string) key) { - 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); + inout(string)[typeof(key)] aa; + + foreach (i; aa.byKey()) {} + foreach (i; aa.byValue()) {} + foreach (i; aa.byKeyValue()) {} } -} -/// ditto -void destroy(T)(ref T obj) - if (!is(T == struct) && !is(T == interface) && !is(T == class) && !_isStaticArray!T) -{ - obj = T.init; + const int[int] caa; + static assert(is(typeof(caa.byValue().front) == const int)); } -template _isStaticArray(T : U[N], U, size_t N) +private void _destructRecurse(E, size_t n)(ref E[n] arr) { - enum bool _isStaticArray = true; -} + import core.internal.traits : hasElaborateDestructor; -template _isStaticArray(T) -{ - enum bool _isStaticArray = false; + static if (hasElaborateDestructor!E) + { + foreach_reverse (ref elem; arr) + _destructRecurse(elem); + } } -unittest +// Public and explicitly undocumented +void _postblitRecurse(S)(ref S s) + if (is(S == struct)) { - { - int a = 42; - destroy(a); - assert(a == 0); - } - { - float a = 42; - destroy(a); - assert(isnan(a)); - } + static if (__traits(hasMember, S, "__xpostblit") && + // Bugzilla 14746: Check that it's the exact member of S. + __traits(isSame, S, __traits(parent, s.__xpostblit))) + s.__xpostblit(); } -version (unittest) +// Ditto +void _postblitRecurse(E, size_t n)(ref E[n] arr) { - private bool isnan(float x) + import core.internal.traits : hasElaborateCopyConstructor; + + static if (hasElaborateCopyConstructor!E) { - return x != x; + size_t i; + scope(failure) + { + for (; i != 0; --i) + { + _destructRecurse(arr[i - 1]); // What to do if this throws? + } + } + + for (i = 0; i < arr.length; ++i) + _postblitRecurse(arr[i]); } } -private +// Test destruction/postblit order +@safe nothrow pure unittest { - extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow; - extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* arrptr) pure nothrow; -} + string[] order; -/** - * (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 -{ - return _d_arraysetcapacity(typeid(T[]), 0, cast(void[]*)&arr); -} -/// -@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); + struct InnerTop + { + ~this() @safe nothrow pure + { + order ~= "destroy inner top"; + } - //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 + this(this) @safe nothrow pure + { + order ~= "copy inner top"; + } + } + + struct InnerMiddle {} + + version(none) // https://issues.dlang.org/show_bug.cgi?id=14242 + struct InnerElement { - assert(a.capacity != 0); - assert(a.capacity == b.capacity + 1); //both a and b share the same tail + static char counter = '1'; + + ~this() @safe nothrow pure + { + order ~= "destroy inner element #" ~ counter++; + } + + this(this) @safe nothrow pure + { + order ~= "copy inner element #" ~ counter++; + } } - 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 -{ - return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void[]*)&arr); -} -/// -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.ptr !is slice.ptr); - assert(slice.capacity == u); + struct InnerBottom + { + ~this() @safe nothrow pure + { + order ~= "destroy inner bottom"; + } + + this(this) @safe nothrow pure + { + order ~= "copy inner bottom"; + } + } + + struct S + { + char[] s; + InnerTop top; + InnerMiddle middle; + version(none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242 + int a; + InnerBottom bottom; + ~this() @safe nothrow pure { order ~= "destroy outer"; } + this(this) @safe nothrow pure { order ~= "copy outer"; } + } - //Dynamic array slices - int[] a = [1, 2, 3, 4]; - a.reserve(8); //prepare a for appending 4 more items - auto p = a.ptr; - u = a.capacity; - a ~= [5, 6, 7, 8]; - assert(p == a.ptr); //a should not have been reallocated - assert(u == a.capacity); //a should not have been extended -} + string[] destructRecurseOrder; + { + S s; + _destructRecurse(s); + destructRecurseOrder = order; + order = null; + } -// Issue 6646: should be possible to use array.reserve from SafeD. -@safe unittest -{ - int[] a; - a.reserve(10); + assert(order.length); + assert(destructRecurseOrder == order); + order = null; + + S s; + _postblitRecurse(s); + assert(order.length); + auto postblitRecurseOrder = order; + order = null; + S s2 = s; + assert(order.length); + assert(postblitRecurseOrder == order); } -/** - * 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 +// Test static struct +nothrow @safe @nogc unittest { - _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr)); - return arr; + static int i = 0; + static struct S { ~this() nothrow @safe @nogc { i = 42; } } + S s; + _destructRecurse(s); + assert(i == 42); } -/// + 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 + // Bugzilla 14746 + static struct HasDtor { - // With assumeSafeAppend. Appending overwrites. - int[] c = a [0 .. 3]; - c.assumeSafeAppend() ~= 5; - assert(a.ptr == c.ptr); + ~this() { assert(0); } + } + static struct Owner + { + HasDtor* ptr; + alias ptr this; } -} -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); + Owner o; + assert(o.ptr is null); + destroy(o); // must not reach in HasDtor.__dtor() } unittest { - int[] arr = [1, 2, 3]; - void foo(ref int[] i) + // Bugzilla 14746 + static struct HasPostblit { - i ~= 5; + this(this) { assert(0); } } - arr = arr[0 .. 2]; - foo(assumeSafeAppend(arr)); //pass by ref - assert(arr[]==[1, 2, 5]); - arr = arr[0 .. 1].assumeSafeAppend(); //pass by value + static struct Owner + { + HasPostblit* ptr; + alias ptr this; + } + + Owner o; + assert(o.ptr is null); + _postblitRecurse(o); // must not reach in HasPostblit.__postblit() } -// https://issues.dlang.org/show_bug.cgi?id=10574 +// Test handling of fixed-length arrays +// Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242 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[]))); -} + string[] order; -version (none) -{ - // enforce() copied from Phobos std.contracts for destroy(), left out until - // we decide whether to use it. + struct S + { + char id; + this(this) + { + order ~= "copy #" ~ id; + } - T _enforce(T, string file = __FILE__, int line = __LINE__) - (T value, lazy const(char)[] msg = null) - { - if (!value) bailOut(file, line, msg); - return value; + ~this() + { + order ~= "destroy #" ~ id; + } } - T _enforce(T, string file = __FILE__, int line = __LINE__) - (T value, scope void delegate() dg) + string[] destructRecurseOrder; { - if (!value) dg(); - return value; + S[3] arr = [S('1'), S('2'), S('3')]; + _destructRecurse(arr); + destructRecurseOrder = order; + order = null; } + assert(order.length); + assert(destructRecurseOrder == order); + order = null; - T _enforce(T)(T value, lazy Exception ex) + S[3] arr = [S('1'), S('2'), S('3')]; + _postblitRecurse(arr); + assert(order.length); + auto postblitRecurseOrder = order; + order = null; + + auto arrCopy = arr; + assert(order.length); + assert(postblitRecurseOrder == order); +} + +// Test handling of failed postblit +// Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242 +/+ nothrow @safe +/ unittest +{ + static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } } + static string[] order; + static struct Inner { - if (!value) throw ex(); - return value; + char id; + + @safe: + this(this) + { + order ~= "copy inner #" ~ id; + if(id == '2') + throw new FailedPostblitException(); + } + + ~this() nothrow + { + order ~= "destroy inner #" ~ id; + } } - private void _bailOut(string file, int line, in char[] msg) + static struct Outer { - char[21] buf; - throw new Exception(cast(string)(file ~ "(" ~ ulongToString(buf[], line) ~ "): " ~ (msg ? msg : "Enforcement failed"))); + Inner inner1, inner2, inner3; + + nothrow @safe: + this(char first, char second, char third) + { + inner1 = Inner(first); + inner2 = Inner(second); + inner3 = Inner(third); + } + + this(this) + { + order ~= "copy outer"; + } + + ~this() + { + order ~= "destroy outer"; + } } -} + auto outer = Outer('1', '2', '3'); -/*************************************** - * Helper function used to see if two containers of different - * types have the same contents in the same sequence. - */ + try _postblitRecurse(outer); + catch(FailedPostblitException) {} + catch(Exception) assert(false); -bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) -{ - if (a1.length != a2.length) - return false; + auto postblitRecurseOrder = order; + order = null; + + try auto copy = outer; + catch(FailedPostblitException) {} + catch(Exception) assert(false); + + assert(postblitRecurseOrder == order); + order = null; + + Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')]; + + try _postblitRecurse(arr); + catch(FailedPostblitException) {} + catch(Exception) assert(false); + + postblitRecurseOrder = order; + order = null; + + try auto arrCopy = arr; + catch(FailedPostblitException) {} + catch(Exception) assert(false); - // This is function is used as a compiler intrinsic and explicitly written - // in a lowered flavor to use as few CTFE instructions as possible. - size_t idx = 0; - immutable length = a1.length; + assert(postblitRecurseOrder == order); +} - for(;idx < length;++idx) +version (unittest) +{ + private bool isnan(float x) { - if (a1[idx] != a2[idx]) - return false; + return x != x; } - return true; } -/** -Calculates the hash value of $(D arg) with $(D seed) initial value. -The result may not be equal to `typeid(T).getHash(&arg)`. - -Params: - arg = argument to calculate the hash value of - seed = the $(D seed) value (may be used for hash chaining) - -Return: calculated hash value of $(D arg) -*/ -size_t hashOf(T)(auto ref T arg, size_t seed = 0) +private { - import core.internal.hash; - return core.internal.hash.hashOf(arg, seed); + extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow; + extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* arrptr) pure nothrow; } +/** + * (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 +{ + return _d_arraysetcapacity(typeid(T[]), 0, cast(void[]*)&arr); +} /// -@system unittest +@safe unittest { - class MyObject - { - size_t myMegaHash() const @safe pure nothrow - { - return 42; - } - } - struct Test + //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 { - 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; - } + 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. } -unittest +/** + * 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 { - // Issue # 16654 / 16764 - auto a = [1]; - auto b = a.dup; - assert(hashOf(a) == hashOf(b)); + return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void[]*)&arr); } - -bool _xopEquals(in void*, in void*) +/// +unittest { - throw new Error("TypeInfo.equals is not implemented"); + //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.ptr !is slice.ptr); + 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.ptr; + u = a.capacity; + a ~= [5, 6, 7, 8]; + assert(p == a.ptr); //a should not have been reallocated + assert(u == a.capacity); //a should not have been extended } -bool _xopCmp(in void*, in void*) +// Issue 6646: should be possible to use array.reserve from SafeD. +@safe unittest { - throw new Error("TypeInfo.compare is not implemented"); + int[] a; + a.reserve(10); } -void __ctfeWrite(scope const(char)[] s) @nogc @safe pure nothrow {} - -/****************************************** - * Create RTInfo for type T +/** + * 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. */ - -template RTInfo(T) +auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow { - enum RTInfo = null; + _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr)); + return arr; } - -// lhs == rhs lowers to __equals(lhs, rhs) for dynamic arrays -bool __equals(T1, T2)(T1[] lhs, T2[] rhs) +/// +unittest { - import core.internal.traits : Unqual; - alias U1 = Unqual!T1; - alias U2 = Unqual!T2; - - static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } - static @trusted R trustedCast(R, S)(S[] r) { return cast(R) r; } - - if (lhs.length != rhs.length) - return false; - - if (lhs.length == 0 && rhs.length == 0) - return true; - - static if (is(U1 == void) && is(U2 == void)) - { - return __equals(trustedCast!(ubyte[])(lhs), trustedCast!(ubyte[])(rhs)); - } - else static if (is(U1 == void)) - { - return __equals(trustedCast!(ubyte[])(lhs), rhs); - } - else static if (is(U2 == void)) - { - return __equals(lhs, trustedCast!(ubyte[])(rhs)); - } - else static if (!is(U1 == U2)) - { - // This should replace src/object.d _ArrayEq which - // compares arrays of different types such as long & int, - // char & wchar. - // Compiler lowers to __ArrayEq in dmd/src/opover.d - foreach (const u; 0 .. lhs.length) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - return true; - } - else static if (__traits(isIntegral, U1)) - { - - if (!__ctfe) - { - import core.stdc.string : memcmp; - return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }(); - } - else - { - foreach (const u; 0 .. lhs.length) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - return true; - } - } - else - { - foreach (const u; 0 .. lhs.length) - { - static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u)))) - { - if (!__equals(at(lhs, u), at(rhs, u))) - return false; - } - else static if (__traits(isFloating, U1)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else static if (is(U1 : Object) && is(U2 : Object)) - { - if (!(cast(Object)at(lhs, u) is cast(Object)at(rhs, u) - || at(lhs, u) && (cast(Object)at(lhs, u)).opEquals(cast(Object)at(rhs, u)))) - return false; - } - else static if (__traits(hasMember, U1, "opEquals")) - { - if (!at(lhs, u).opEquals(at(rhs, u))) - return false; - } - else static if (is(U1 == delegate)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else static if (is(U1 == U11*, U11)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else static if (__traits(isAssociativeArray, U1)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else - { - if (at(lhs, u).tupleof != at(rhs, u).tupleof) - return false; - } - } + int[] a = [1, 2, 3, 4]; - return true; - } -} + // Without assumeSafeAppend. Appending relocates. + int[] b = a [0 .. 3]; + b ~= 5; + assert(a.ptr != b.ptr); -unittest { - assert(__equals([], [])); - assert(!__equals([1, 2], [1, 2, 3])); + debug(SENTINEL) {} else + { + // With assumeSafeAppend. Appending overwrites. + int[] c = a [0 .. 3]; + c.assumeSafeAppend() ~= 5; + assert(a.ptr == c.ptr); + } } unittest { - struct A - { - int a; - } - - auto arr1 = [A(0), A(2)]; - auto arr2 = [A(0), A(1)]; - auto arr3 = [A(0), A(1)]; - - assert(arr1 != arr2); - assert(arr2 == arr3); + 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); } unittest { - struct A + int[] arr = [1, 2, 3]; + void foo(ref int[] i) { - int a; - int b; - - bool opEquals(const A other) - { - return this.a == other.b && this.b == other.a; - } + i ~= 5; } - - auto arr1 = [A(1, 0), A(0, 1)]; - auto arr2 = [A(1, 0), A(0, 1)]; - auto arr3 = [A(0, 1), A(1, 0)]; - - assert(arr1 != arr2); - assert(arr2 == arr3); + 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=18252 +// https://issues.dlang.org/show_bug.cgi?id=10574 unittest { - string[int][] a1, a2; - assert(__equals(a1, a2)); - assert(a1 == a2); - a1 ~= [0: "zero"]; - a2 ~= [0: "zero"]; - assert(__equals(a1, a2)); - assert(a1 == a2); - a2[0][1] = "one"; - assert(!__equals(a1, a2)); - assert(a1 != a2); + 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[]))); } -// Compare class and interface objects for ordering. -private int __cmp(Obj)(Obj lhs, Obj rhs) -if (is(Obj : Object)) +version (none) { - if (lhs is rhs) - return 0; - // Regard null references as always being "less than" - if (!lhs) - return -1; - if (!rhs) - return 1; - return lhs.opCmp(rhs); -} + // enforce() copied from Phobos std.contracts for destroy(), left out until + // we decide whether to use it. -int __cmp(T)(const T[] lhs, const T[] rhs) @trusted -if (__traits(isScalar, T)) -{ - // Compute U as the implementation type for T - static if (is(T == ubyte) || is(T == void) || is(T == bool)) - alias U = char; - else static if (is(T == wchar)) - alias U = ushort; - else static if (is(T == dchar)) - alias U = uint; - else static if (is(T == ifloat)) - alias U = float; - else static if (is(T == idouble)) - alias U = double; - else static if (is(T == ireal)) - alias U = real; - else - alias U = T; - static if (is(U == char)) + T _enforce(T, string file = __FILE__, int line = __LINE__) + (T value, lazy const(char)[] msg = null) { - import core.internal.string : dstrcmp; - return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); + if (!value) bailOut(file, line, msg); + return value; } - else static if (!is(U == T)) + + T _enforce(T, string file = __FILE__, int line = __LINE__) + (T value, scope void delegate() dg) { - // Reuse another implementation - return __cmp(cast(U[]) lhs, cast(U[]) rhs); + if (!value) dg(); + return value; } - else + + T _enforce(T)(T value, lazy Exception ex) { - immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; - foreach (const u; 0 .. len) - { - static if (__traits(isFloating, T)) - { - immutable a = lhs.ptr[u], b = rhs.ptr[u]; - static if (is(T == cfloat) || is(T == cdouble) - || is(T == creal)) - { - // Use rt.cmath2._Ccmp instead ? - auto r = (a.re > b.re) - (a.re < b.re); - if (!r) r = (a.im > b.im) - (a.im < b.im); - } - else - { - const r = (a > b) - (a < b); - } - if (r) return r; - } - else if (lhs.ptr[u] != rhs.ptr[u]) - return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1; - } - return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length); + if (!value) throw ex(); + return value; } -} - -// This function is called by the compiler when dealing with array -// comparisons in the semantic analysis phase of CmpExp. The ordering -// comparison is lowered to a call to this template. -int __cmp(T1, T2)(T1[] s1, T2[] s2) -if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) -{ - import core.internal.traits : Unqual; - alias U1 = Unqual!T1; - alias U2 = Unqual!T2; - - static if (is(U1 == void) && is(U2 == void)) - static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; } - else - static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } - - // All unsigned byte-wide types = > dstrcmp - immutable len = s1.length <= s2.length ? s1.length : s2.length; - foreach (const u; 0 .. len) + private void _bailOut(string file, int line, in char[] msg) { - static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) - { - auto c = __cmp(at(s1, u), at(s2, u)); - if (c != 0) - return c; - } - else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) - { - auto c = at(s1, u).opCmp(at(s2, u)); - if (c != 0) - return c; - } - else static if (__traits(compiles, at(s1, u) < at(s2, u))) - { - if (at(s1, u) != at(s2, u)) - return at(s1, u) < at(s2, u) ? -1 : 1; - } - else - { - // TODO: fix this legacy bad behavior, see - // https://issues.dlang.org/show_bug.cgi?id=17244 - static assert(is(U1 == U2), "Internal error."); - import core.stdc.string : memcmp; - auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))(); - if (c != 0) - return c; - } + char[21] buf; + throw new Exception(cast(string)(file ~ "(" ~ ulongToString(buf[], line) ~ "): " ~ (msg ? msg : "Enforcement failed"))); } - return s1.length < s2.length ? -1 : (s1.length > s2.length); } -// integral types -@safe unittest -{ - void compareMinMax(T)() - { - T[2] a = [T.max, T.max]; - T[2] b = [T.min, T.min]; +/** +Calculates the hash value of $(D arg) with $(D seed) initial value. +The result may not be equal to `typeid(T).getHash(&arg)`. - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); - } +Params: + arg = argument to calculate the hash value of + seed = the $(D seed) value (may be used for hash chaining) - compareMinMax!int; - compareMinMax!uint; - compareMinMax!long; - compareMinMax!ulong; - compareMinMax!short; - compareMinMax!ushort; - compareMinMax!byte; - compareMinMax!dchar; - compareMinMax!wchar; +Return: calculated hash value of $(D arg) +*/ +size_t hashOf(T)(auto ref T arg, size_t seed = 0) +{ + import core.internal.hash; + return core.internal.hash.hashOf(arg, seed); } -// char types (dstrcmp) -@safe unittest +/// +@system unittest { - void compareMinMax(T)() + class MyObject { - T[2] a = [T.max, T.max]; - T[2] b = [T.min, T.min]; - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); + size_t myMegaHash() const @safe pure nothrow + { + return 42; + } } - - compareMinMax!ubyte; - compareMinMax!bool; - compareMinMax!char; - compareMinMax!(const char); - - string s1 = "aaaa"; - string s2 = "bbbb"; - assert(__cmp(s2, s1) > 0); - assert(__cmp(s1, s2) < 0); -} - -// fp types -@safe unittest -{ - void compareMinMax(T)() + struct Test { - T[2] a = [T.max, T.max]; - T[2] b = [T.min_normal, T.min_normal]; - T[2] c = [T.max, T.min_normal]; - T[1] d = [T.max]; - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); - assert(__cmp(a, c) > 0); - assert(__cmp(a, d) > 0); - assert(__cmp(d, c) < 0); - assert(__cmp(c, c) == 0); + 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; + } } - - compareMinMax!real; - compareMinMax!float; - compareMinMax!double; - compareMinMax!ireal; - compareMinMax!ifloat; - compareMinMax!idouble; - compareMinMax!creal; - //compareMinMax!cfloat; - compareMinMax!cdouble; - - // qualifiers - compareMinMax!(const real); - compareMinMax!(immutable real); } -// void[] -@safe unittest +unittest { - void[] a; - const(void)[] b; - - (() @trusted - { - a = cast(void[]) "bb"; - b = cast(const(void)[]) "aa"; - })(); - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); + // Issue # 16654 / 16764 + auto a = [1]; + auto b = a.dup; + assert(hashOf(a) == hashOf(b)); } -// arrays of arrays with mixed modifiers -@safe unittest +bool _xopEquals(in void*, in void*) { - // https://issues.dlang.org/show_bug.cgi?id=17876 - bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; } - bool less2(const void[][] a, void[][] b) { return a < b; } - bool less3(inout size_t[][] a, size_t[][] b) { return a < b; } - - immutable size_t[][] a = [[1, 2], [3, 4]]; - size_t[][] b = [[1, 2], [3, 5]]; - assert(less1(a, b)); - assert(less3(a, b)); - - auto va = [cast(immutable void[])a[0], a[1]]; - auto vb = [cast(void[])b[0], b[1]]; - assert(less2(va, vb)); + throw new Error("TypeInfo.equals is not implemented"); } -// objects -@safe unittest +bool _xopCmp(in void*, in void*) { - 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); + throw new Error("TypeInfo.compare is not implemented"); } -// structs -@safe unittest -{ - struct C - { - ubyte i; - this(ubyte i) { this.i = i; } - } +void __ctfeWrite(scope const(char)[] s) @nogc @safe pure nothrow {} - auto c1 = C(1); - auto c2 = C(2); +/****************************************** + * Create RTInfo for type T + */ - assert(__cmp([c1, c1][], [c2, c2][]) < 0); - assert(__cmp([c2, c2], [c1, c1]) > 0); - assert(__cmp([c2, c2], [c2, c1]) > 0); +template RTInfo(T) +{ + enum RTInfo = null; } // Compiler hook into the runtime implementation of array (vector) operations.