summaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/object.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/libdruntime/object.d')
-rw-r--r--libphobos/libdruntime/object.d3555
1 files changed, 2163 insertions, 1392 deletions
diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d
index e96d1c48563..151755feed0 100644
--- a/libphobos/libdruntime/object.d
+++ b/libphobos/libdruntime/object.d
@@ -1,21 +1,62 @@
/**
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ * $(DIVC quickindex,
+ * $(BOOKTABLE,
+ * $(TR $(TH Category) $(TH Symbols))
+ * $(TR $(TD Arrays) $(TD
+ * $(MYREF assumeSafeAppend)
+ * $(MYREF capacity)
+ * $(MYREF idup)
+ * $(MYREF reserve)
+ * ))
+ * $(TR $(TD Associative arrays) $(TD
+ * $(MYREF byKey)
+ * $(MYREF byKeyValue)
+ * $(MYREF byValue)
+ * $(MYREF clear)
+ * $(MYREF get)
+ * $(MYREF keys)
+ * $(MYREF rehash)
+ * $(MYREF require)
+ * $(MYREF update)
+ * $(MYREF values)
+ * ))
+ * $(TR $(TD General) $(TD
+ * $(MYREF destroy)
+ * $(MYREF dup)
+ * $(MYREF hashOf)
+ * $(MYREF opEquals)
+ * ))
+ * $(TR $(TD Types) $(TD
+ * $(MYREF Error)
+ * $(MYREF Exception)
+ * $(MYREF noreturn)
+ * $(MYREF Object)
+ * $(MYREF Throwable)
+ * ))
+ * $(TR $(TD Type info) $(TD
+ * $(MYREF Interface)
+ * $(MYREF ModuleInfo)
+ * $(MYREF OffsetTypeInfo)
+ * $(MYREF RTInfoImpl)
+ * $(MYREF rtinfoNoPointers)
+ * $(MYREF TypeInfo)
+ * $(MYREF TypeInfo_Class)
+ * ))
+ * ))
+ *
* Forms the symbols available to all D programs. Includes Object, which is
* the root of the class object hierarchy. This module is implicitly
* imported.
*
* Copyright: Copyright Digital Mars 2000 - 2011.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC object.d)
*/
module object;
-private
-{
- extern (C) Object _d_newclass(const TypeInfo_Class ci);
- extern (C) void rt_finalize(void *data, bool det=true);
-}
-
alias size_t = typeof(int.sizeof);
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);
@@ -29,7 +70,12 @@ alias string = immutable(char)[];
alias wstring = immutable(wchar)[];
alias dstring = immutable(dchar)[];
-version (D_ObjectiveC) public import core.attribute : selector;
+version (D_ObjectiveC)
+{
+ deprecated("explicitly import `selector` instead using: `import core.attribute : selector;`")
+ public import core.attribute : selector;
+}
+version (Posix) public import core.attribute : gnuAbiTag;
// Some ABIs use a complex varargs implementation requiring TypeInfo.argTypes().
version (GNU)
@@ -65,6 +111,20 @@ class Object
return typeid(this).name;
}
+ @system unittest
+ {
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object.Object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ class C {}
+
+ Object obj = new Object;
+ C c = new C;
+
+ assert(obj.toString() == "object.Object");
+ assert(c.toString() == fqn_unittest ~ ".C");
+ }
+
/**
* Compute hash function for Object.
*/
@@ -100,6 +160,23 @@ class Object
//return this !is o;
}
+ @system unittest
+ {
+ Object obj = new Object;
+
+ bool gotCaught;
+ try
+ {
+ obj.opCmp(new Object);
+ }
+ catch (Exception e)
+ {
+ gotCaught = true;
+ assert(e.msg == "need opCmp for class object.Object");
+ }
+ assert(gotCaught);
+ }
+
/**
* Test whether $(D this) is equal to $(D o).
* The default implementation only compares by identity (using the $(D is) operator).
@@ -149,9 +226,18 @@ class Object
}
return null;
}
+
+ @system unittest
+ {
+ Object valid_obj = Object.factory("object.Object");
+ Object invalid_obj = Object.factory("object.__this_class_doesnt_exist__");
+
+ assert(valid_obj !is null);
+ assert(invalid_obj is null);
+ }
}
-auto opEquals(Object lhs, Object rhs)
+bool opEquals(Object lhs, Object rhs)
{
// If aliased to the same object or both null => equal
if (lhs is rhs) return true;
@@ -159,6 +245,8 @@ auto opEquals(Object lhs, Object rhs)
// If either is null => non-equal
if (lhs is null || rhs is null) return false;
+ if (!lhs.opEquals(rhs)) return false;
+
// If same exact type => one call to method opEquals
if (typeid(lhs) is typeid(rhs) ||
!__ctfe && typeid(lhs).opEquals(typeid(rhs)))
@@ -166,22 +254,116 @@ auto opEquals(Object lhs, Object rhs)
(issue 7147). But CTFE also guarantees that equal TypeInfos are
always identical. So, no opEquals needed during CTFE. */
{
- return lhs.opEquals(rhs);
+ return true;
}
// General case => symmetric calls to method opEquals
- return lhs.opEquals(rhs) && rhs.opEquals(lhs);
+ return rhs.opEquals(lhs);
}
/************************
* Returns true if lhs and rhs are equal.
*/
-auto opEquals(const Object lhs, const Object rhs)
+bool opEquals(const Object lhs, const Object rhs)
{
// A hack for the moment.
return opEquals(cast()lhs, cast()rhs);
}
+/// If aliased to the same object or both null => equal
+@system unittest
+{
+ class F { int flag; this(int flag) { this.flag = flag; } }
+
+ F f;
+ assert(f == f); // both null
+ f = new F(1);
+ assert(f == f); // both aliased to the same object
+}
+
+/// If either is null => non-equal
+@system unittest
+{
+ class F { int flag; this(int flag) { this.flag = flag; } }
+ F f;
+ assert(!(new F(0) == f));
+ assert(!(f == new F(0)));
+}
+
+/// If same exact type => one call to method opEquals
+@system unittest
+{
+ class F
+ {
+ int flag;
+
+ this(int flag)
+ {
+ this.flag = flag;
+ }
+
+ override bool opEquals(const Object o)
+ {
+ return flag == (cast(F) o).flag;
+ }
+ }
+
+ F f;
+ assert(new F(0) == new F(0));
+ assert(!(new F(0) == new F(1)));
+}
+
+/// General case => symmetric calls to method opEquals
+@system unittest
+{
+ int fEquals, gEquals;
+
+ class Base
+ {
+ int flag;
+ this(int flag)
+ {
+ this.flag = flag;
+ }
+ }
+
+ class F : Base
+ {
+ this(int flag) { super(flag); }
+
+ override bool opEquals(const Object o)
+ {
+ fEquals++;
+ return flag == (cast(Base) o).flag;
+ }
+ }
+
+ class G : Base
+ {
+ this(int flag) { super(flag); }
+
+ override bool opEquals(const Object o)
+ {
+ gEquals++;
+ return flag == (cast(Base) o).flag;
+ }
+ }
+
+ assert(new F(1) == new G(1));
+ assert(fEquals == 1);
+ assert(gEquals == 1);
+}
+
+// To cover const Object opEquals
+@system unittest
+{
+ const Object obj1 = new Object;
+ const Object obj2 = new Object;
+
+ assert(obj1 == obj1);
+ assert(obj1 != obj2);
+}
+
private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow;
void setSameMutex(shared Object ownee, shared Object owner)
@@ -189,6 +371,24 @@ void setSameMutex(shared Object ownee, shared Object owner)
_d_setSameMutex(ownee, owner);
}
+@system unittest
+{
+ shared Object obj1 = new Object;
+ synchronized class C
+ {
+ void bar() {}
+ }
+ shared C obj2 = new shared(C);
+ obj2.bar();
+
+ assert(obj1.__monitor != obj2.__monitor);
+ assert(obj1.__monitor is null);
+
+ setSameMutex(obj1, obj2);
+ assert(obj1.__monitor == obj2.__monitor);
+ assert(obj1.__monitor !is null);
+}
+
/**
* Information about an interface.
* When an object is accessed via an interface, an Interface* appears as the
@@ -218,7 +418,7 @@ struct OffsetTypeInfo
*/
class TypeInfo
{
- override string toString() const pure @safe nothrow
+ override string toString() const @safe nothrow
{
return typeid(this).name;
}
@@ -228,18 +428,21 @@ class TypeInfo
return hashOf(this.toString());
}
- override int opCmp(Object o)
+ override int opCmp(Object rhs)
{
- import core.internal.traits : externDFunc;
- alias dstrcmp = externDFunc!("core.internal.string.dstrcmp",
- int function(scope const char[] s1, scope const char[] s2) @trusted pure nothrow @nogc);
-
- if (this is o)
+ if (this is rhs)
return 0;
- TypeInfo ti = cast(TypeInfo)o;
+ auto ti = cast(TypeInfo) rhs;
if (ti is null)
return 1;
- return dstrcmp(this.toString(), ti.toString());
+ return __cmp(this.toString(), ti.toString());
+ }
+
+ @system unittest
+ {
+ assert(typeid(void) <= typeid(void));
+ assert(typeid(void).opCmp(null));
+ assert(!typeid(void).opCmp(typeid(void)));
}
override bool opEquals(Object o)
@@ -254,6 +457,14 @@ class TypeInfo
return ti && this.toString() == ti.toString();
}
+ @system unittest
+ {
+ auto anotherObj = new Object();
+
+ assert(typeid(void).opEquals(typeid(void)));
+ assert(!typeid(void).opEquals(anotherObj));
+ }
+
/**
* Computes a hash of the instance of a type.
* Params:
@@ -280,8 +491,22 @@ class TypeInfo
/// Swaps two instances of the type.
void swap(void* p1, void* p2) const
{
- immutable size_t n = tsize;
- for (size_t i = 0; i < n; i++)
+ size_t remaining = tsize;
+ // If the type might contain pointers perform the swap in pointer-sized
+ // chunks in case a garbage collection pass interrupts this function.
+ if ((cast(size_t) p1 | cast(size_t) p2) % (void*).alignof == 0)
+ {
+ while (remaining >= (void*).sizeof)
+ {
+ void* tmp = *cast(void**) p1;
+ *cast(void**) p1 = *cast(void**) p2;
+ *cast(void**) p2 = tmp;
+ p1 += (void*).sizeof;
+ p2 += (void*).sizeof;
+ remaining -= (void*).sizeof;
+ }
+ }
+ for (size_t i = 0; i < remaining; i++)
{
byte t = (cast(byte *)p1)[i];
(cast(byte*)p1)[i] = (cast(byte*)p2)[i];
@@ -289,6 +514,36 @@ class TypeInfo
}
}
+ @system unittest
+ {
+ class _TypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ @property override size_t tsize() nothrow pure const @safe @nogc { return tsize_val; }
+
+ size_t tsize_val;
+ }
+ auto dummy = new _TypeInfo_Dummy();
+ cast(void)dummy.initializer(); // For coverage completeness
+
+ int a = 2, b = -2;
+ dummy.swap(&a, &b);
+ // does nothing because tsize is 0
+ assert(a == 2);
+ assert(b == -2);
+
+ dummy.tsize_val = int.sizeof;
+ dummy.swap(&a, &b);
+ assert(a == -2);
+ assert(b == 2);
+
+ void* ptr_a = null, ptr_b = cast(void*)1;
+ dummy.tsize_val = (void*).sizeof;
+ dummy.swap(&ptr_a, &ptr_b);
+ assert(ptr_a is cast(void*)1);
+ assert(ptr_b is null);
+ }
+
/** Get TypeInfo for 'next' type, as defined by what kind of type this is,
null if none. */
@property inout(TypeInfo) next() nothrow pure inout @nogc { return null; }
@@ -302,7 +557,7 @@ class TypeInfo
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 */
+ 2 means arg of this type is passed in SIMD register(s) if available */
@property uint flags() nothrow pure const @safe @nogc { return 0; }
/// Get type information on the contents of the type; null if not available
@@ -330,9 +585,87 @@ class TypeInfo
@property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return rtinfoHasPointers; } // better safe than sorry
}
+@system unittest
+{
+ class _TypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ }
+ auto dummy = new _TypeInfo_Dummy();
+ cast(void)dummy.initializer(); // For coverage completeness
+
+ assert(dummy.rtInfo() is rtinfoHasPointers);
+ assert(typeid(void).rtInfo() is rtinfoNoPointers);
+
+ assert(dummy.tsize() == 0);
+
+ bool gotCaught;
+ try
+ {
+ dummy.compare(null, null);
+ } catch (Error e)
+ {
+ gotCaught = true;
+ assert(e.msg == "TypeInfo.compare is not implemented");
+ }
+ assert(gotCaught);
+
+ assert(dummy.equals(null, null));
+ assert(!dummy.equals(cast(void*)1, null));
+}
+
+@system unittest
+{
+ assert(typeid(void).next() is null);
+ assert(typeid(void).offTi() is null);
+ assert(typeid(void).tsize() == 1);
+
+ version (WithArgTypes)
+ {
+ TypeInfo ti1;
+ TypeInfo ti2;
+ assert(typeid(void).argTypes(ti1, ti2) == 0);
+ assert(typeid(void) is ti1);
+
+ assert(ti1 !is null);
+ assert(ti2 is null);
+ }
+}
+
+@system unittest
+{
+ class _ZypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ }
+ auto dummy2 = new _ZypeInfo_Dummy();
+ cast(void)dummy2.initializer(); // For coverage completeness
+
+ assert(typeid(void) > dummy2);
+ assert(dummy2 < typeid(void));
+}
+
+@safe unittest
+{
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ class _TypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ }
+
+ auto dummy = new _TypeInfo_Dummy();
+ cast(void)dummy.initializer(); // For coverage completeness
+
+ assert(dummy.toString() == fqn_unittest ~ "._TypeInfo_Dummy");
+ assert(dummy.toHash() == hashOf(dummy.toString()));
+ assert(dummy.getHash(null) == 0);
+}
+
class TypeInfo_Enum : TypeInfo
{
- override string toString() const { return name; }
+ override string toString() const pure { return name; }
override bool opEquals(Object o)
{
@@ -343,15 +676,117 @@ class TypeInfo_Enum : TypeInfo
this.base == c.base;
}
+ @system unittest
+ {
+ enum E { A, B, C }
+ enum EE { A, B, C }
+
+ assert(typeid(E).opEquals(typeid(E)));
+ assert(!typeid(E).opEquals(typeid(EE)));
+ }
+
override size_t getHash(scope const void* p) const { return base.getHash(p); }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+ E e1 = E.A;
+ E e2 = E.B;
+
+ assert(typeid(E).getHash(&e1) == hashOf(E.A));
+ assert(typeid(E).getHash(&e2) == hashOf(E.B));
+
+ enum ES : string { A = "foo", B = "bar" }
+ ES es1 = ES.A;
+ ES es2 = ES.B;
+
+ assert(typeid(ES).getHash(&es1) == hashOf("foo"));
+ assert(typeid(ES).getHash(&es2) == hashOf("bar"));
+ }
+
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ E e1 = E.A;
+ E e2 = E.B;
+
+ assert(typeid(E).equals(&e1, &e1));
+ assert(!typeid(E).equals(&e1, &e2));
+ }
+
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ E e1 = E.A;
+ E e2 = E.B;
+
+ assert(typeid(E).compare(&e1, &e1) == 0);
+ assert(typeid(E).compare(&e1, &e2) < 0);
+ assert(typeid(E).compare(&e2, &e1) > 0);
+ }
+
override @property size_t tsize() nothrow pure const { return base.tsize; }
+
+ @safe unittest
+ {
+ enum E { A, B, C }
+ enum ES : string { A = "a", B = "b", C = "c"}
+
+ assert(typeid(E).tsize == E.sizeof);
+ assert(typeid(ES).tsize == ES.sizeof);
+ assert(typeid(E).tsize != ES.sizeof);
+ }
+
override void swap(void* p1, void* p2) const { return base.swap(p1, p2); }
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ E e1 = E.A;
+ E e2 = E.B;
+
+ typeid(E).swap(&e1, &e2);
+ assert(e1 == E.B);
+ assert(e2 == E.A);
+ }
+
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ assert(typeid(E).next is null);
+ }
+
override @property uint flags() nothrow pure const { return base.flags; }
+ @safe unittest
+ {
+ enum E { A, B, C }
+
+ assert(typeid(E).flags == 0);
+ }
+
+ override const(OffsetTypeInfo)[] offTi() const { return base.offTi; }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ assert(typeid(E).offTi is null);
+ }
+
+ override void destroy(void* p) const { return base.destroy(p); }
+ override void postblit(void* p) const { return base.postblit(p); }
+
override const(void)[] initializer() const
{
return m_init.length ? m_init : base.initializer();
@@ -371,7 +806,19 @@ class TypeInfo_Enum : TypeInfo
void[] m_init;
}
-unittest // issue 12233
+@safe unittest
+{
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ enum E { A, B, C }
+ enum EE { A, B, C }
+
+ assert(typeid(E).toString() == fqn_unittest ~ ".E");
+}
+
+
+@safe unittest // issue 12233
{
static assert(is(typeof(TypeInfo.init) == TypeInfo));
assert(TypeInfo.init is null);
@@ -404,12 +851,8 @@ class TypeInfo_Pointer : TypeInfo
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;
- else
- return 0;
+ const v1 = *cast(void**) p1, v2 = *cast(void**) p2;
+ return (v1 > v2) - (v1 < v2);
}
override @property size_t tsize() nothrow pure const
@@ -483,7 +926,7 @@ class TypeInfo_Array : TypeInfo
if (result)
return result;
}
- return cast(int)a1.length - cast(int)a2.length;
+ return (a1.length > a2.length) - (a1.length < a2.length);
}
override @property size_t tsize() nothrow pure const
@@ -531,12 +974,12 @@ class TypeInfo_StaticArray : TypeInfo
{
override string toString() const
{
- import core.internal.traits : externDFunc;
- alias sizeToTempString = externDFunc!("core.internal.string.unsignedToTempString",
- char[] function(ulong, return char[], uint) @safe pure nothrow @nogc);
+ import core.internal.string : unsignedToTempString;
char[20] tmpBuff = void;
- return value.toString() ~ "[" ~ sizeToTempString(len, tmpBuff, 10) ~ "]";
+ const lenString = unsignedToTempString(len, tmpBuff);
+
+ return (() @trusted => cast(string) (value.toString() ~ "[" ~ lenString ~ "]"))();
}
override bool opEquals(Object o)
@@ -585,28 +1028,22 @@ class TypeInfo_StaticArray : TypeInfo
override void swap(void* p1, void* p2) const
{
- 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 remaining = value.tsize * len;
+ void[size_t.sizeof * 4] buffer = void;
+ while (remaining > buffer.length)
{
- size_t o = u * sz;
- memcpy(tmp, p1 + o, sz);
- memcpy(p1 + o, p2 + o, sz);
- memcpy(p2 + o, tmp, sz);
+ memcpy(buffer.ptr, p1, buffer.length);
+ memcpy(p1, p2, buffer.length);
+ memcpy(p2, buffer.ptr, buffer.length);
+ p1 += buffer.length;
+ p2 += buffer.length;
+ remaining -= buffer.length;
}
- if (pbuffer)
- GC.free(pbuffer);
+ memcpy(buffer.ptr, p1, remaining);
+ memcpy(p1, p2, remaining);
+ memcpy(p2, buffer.ptr, remaining);
}
override const(void)[] initializer() nothrow pure const
@@ -656,6 +1093,23 @@ class TypeInfo_StaticArray : TypeInfo
override @property immutable(void)* rtInfo() nothrow pure const @safe { return value.rtInfo(); }
}
+// https://issues.dlang.org/show_bug.cgi?id=21315
+@system unittest
+{
+ int[16] a, b;
+ foreach (int i; 0 .. 16)
+ {
+ a[i] = i;
+ b[i] = ~i;
+ }
+ typeid(int[16]).swap(&a, &b);
+ foreach (int i; 0 .. 16)
+ {
+ assert(a[i] == ~i);
+ assert(b[i] == i);
+ }
+}
+
class TypeInfo_AssociativeArray : TypeInfo
{
override string toString() const
@@ -731,7 +1185,7 @@ class TypeInfo_Vector : TypeInfo
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 @property uint flags() nothrow pure const { return 2; /* passed in SIMD register */ }
override const(void)[] initializer() nothrow pure const
{
@@ -750,14 +1204,14 @@ class TypeInfo_Vector : TypeInfo
class TypeInfo_Function : TypeInfo
{
- override string toString() const
+ override string toString() const pure @trusted
{
import core.demangle : demangleType;
alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
- SafeDemangleFunctionType demangle = ( () @trusted => cast(SafeDemangleFunctionType)(&demangleType) ) ();
+ SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
- return (() @trusted => cast(string)(demangle(deco))) ();
+ return cast(string) demangle(deco);
}
override bool opEquals(Object o)
@@ -790,7 +1244,7 @@ class TypeInfo_Function : TypeInfo
string deco;
}
-unittest
+@safe unittest
{
abstract class C
{
@@ -805,11 +1259,57 @@ unittest
assert(typeid(functionTypes[2]).toString() == "int function(int, int)");
}
+@system unittest
+{
+ abstract class C
+ {
+ void func();
+ void func(int a);
+ }
+
+ alias functionTypes = typeof(__traits(getVirtualFunctions, C, "func"));
+
+ Object obj = typeid(functionTypes[0]);
+ assert(obj.opEquals(typeid(functionTypes[0])));
+ assert(typeid(functionTypes[0]) == typeid(functionTypes[0]));
+ assert(typeid(functionTypes[0]) != typeid(functionTypes[1]));
+
+ assert(typeid(functionTypes[0]).tsize() == 0);
+ assert(typeid(functionTypes[0]).initializer() is null);
+ assert(typeid(functionTypes[0]).rtInfo() is null);
+}
+
class TypeInfo_Delegate : TypeInfo
{
- override string toString() const
+ override string toString() const pure @trusted
+ {
+ import core.demangle : demangleType;
+
+ alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
+ SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
+
+ return cast(string) demangle(deco);
+ }
+
+ @safe unittest
{
- return cast(string)(next.toString() ~ " delegate()");
+ double sqr(double x) { return x * x; }
+ sqr(double.init); // for coverage completeness
+
+ auto delegate_str = "double delegate(double) pure nothrow @nogc @safe";
+
+ assert(typeid(typeof(&sqr)).toString() == delegate_str);
+ assert(delegate_str.hashOf() == typeid(typeof(&sqr)).hashOf());
+ assert(typeid(typeof(&sqr)).toHash() == typeid(typeof(&sqr)).hashOf());
+
+ int g;
+
+ alias delegate_type = typeof((int a, int b) => a + b + g);
+ delegate_str = "int delegate(int, int) pure nothrow @nogc @safe";
+
+ assert(typeid(delegate_type).toString() == delegate_str);
+ assert(delegate_str.hashOf() == typeid(delegate_type).hashOf());
+ assert(typeid(delegate_type).toHash() == typeid(delegate_type).hashOf());
}
override bool opEquals(Object o)
@@ -820,6 +1320,19 @@ class TypeInfo_Delegate : TypeInfo
return c && this.deco == c.deco;
}
+ @system unittest
+ {
+ double sqr(double x) { return x * x; }
+ int dbl(int x) { return x + x; }
+ sqr(double.init); // for coverage completeness
+ dbl(int.init); // for coverage completeness
+
+ Object obj = typeid(typeof(&sqr));
+ assert(obj.opEquals(typeid(typeof(&sqr))));
+ assert(typeid(typeof(&sqr)) == typeid(typeof(&sqr)));
+ assert(typeid(typeof(&dbl)) != typeid(typeof(&sqr)));
+ }
+
override size_t getHash(scope const void* p) @trusted const
{
return hashOf(*cast(void delegate()*)p);
@@ -877,6 +1390,10 @@ class TypeInfo_Delegate : TypeInfo
override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(int delegate()); }
}
+private extern (C) Object _d_newclass(const TypeInfo_Class ci);
+private extern (C) int _d_isbaseof(scope TypeInfo_Class child,
+ scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_
+
/**
* Runtime type information about a class.
* Can be retrieved from an object instance by using the
@@ -884,14 +1401,14 @@ class TypeInfo_Delegate : TypeInfo
*/
class TypeInfo_Class : TypeInfo
{
- override string toString() const { return info.name; }
+ override string toString() const pure { return name; }
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;
+ return c && this.name == c.name;
}
override size_t getHash(scope const void* p) @trusted const
@@ -947,8 +1464,8 @@ class TypeInfo_Class : TypeInfo
return m_offTi;
}
- @property auto info() @safe nothrow pure const { return this; }
- @property auto typeinfo() @safe nothrow pure const { return this; }
+ final @property auto info() @safe @nogc nothrow pure const return { return this; }
+ final @property auto typeinfo() @safe @nogc nothrow pure const return { return this; }
byte[] m_init; /** class static initializer
* (init.length gives size in bytes of class)
@@ -983,7 +1500,7 @@ class TypeInfo_Class : TypeInfo
* Search all modules for TypeInfo_Class corresponding to classname.
* Returns: null if not found
*/
- static const(TypeInfo_Class) find(in char[] classname)
+ static const(TypeInfo_Class) find(const scope char[] classname)
{
foreach (m; ModuleInfo)
{
@@ -1019,11 +1536,41 @@ class TypeInfo_Class : TypeInfo
}
return o;
}
+
+ /**
+ * Returns true if the class described by `child` derives from or is
+ * the class described by this `TypeInfo_Class`. Always returns false
+ * if the argument is null.
+ *
+ * Params:
+ * child = TypeInfo for some class
+ * Returns:
+ * true if the class described by `child` derives from or is the
+ * class described by this `TypeInfo_Class`.
+ */
+ final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
+ {
+ if (m_init.length)
+ {
+ // If this TypeInfo_Class represents an actual class we only need
+ // to check the child and its direct ancestors.
+ for (auto ti = cast() child; ti !is null; ti = ti.base)
+ if (ti is this)
+ return true;
+ return false;
+ }
+ else
+ {
+ // If this TypeInfo_Class is the .info field of a TypeInfo_Interface
+ // we also need to recursively check the child's interfaces.
+ return child !is null && _d_isbaseof(cast() child, this);
+ }
+ }
}
alias ClassInfo = TypeInfo_Class;
-unittest
+@safe unittest
{
// Bugzilla 14401
static class X
@@ -1039,7 +1586,7 @@ unittest
class TypeInfo_Interface : TypeInfo
{
- override string toString() const { return info.name; }
+ override string toString() const pure { return info.name; }
override bool opEquals(Object o)
{
@@ -1108,19 +1655,67 @@ class TypeInfo_Interface : TypeInfo
override @property uint flags() nothrow pure const { return 1; }
TypeInfo_Class info;
+
+ /**
+ * Returns true if the class described by `child` derives from the
+ * interface described by this `TypeInfo_Interface`. Always returns
+ * false if the argument is null.
+ *
+ * Params:
+ * child = TypeInfo for some class
+ * Returns:
+ * true if the class described by `child` derives from the
+ * interface described by this `TypeInfo_Interface`.
+ */
+ final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
+ {
+ return child !is null && _d_isbaseof(cast() child, this.info);
+ }
+
+ /**
+ * Returns true if the interface described by `child` derives from
+ * or is the interface described by this `TypeInfo_Interface`.
+ * Always returns false if the argument is null.
+ *
+ * Params:
+ * child = TypeInfo for some interface
+ * Returns:
+ * true if the interface described by `child` derives from or is
+ * the interface described by this `TypeInfo_Interface`.
+ */
+ final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted
+ {
+ return child !is null && _d_isbaseof(cast() child.info, this.info);
+ }
+}
+
+@safe unittest
+{
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ interface I {}
+
+ assert(fqn_unittest ~ ".I" == typeid(I).info.name);
+ assert((fqn_unittest ~ ".I").hashOf() == typeid(I).hashOf());
+ assert(typeid(I).toHash() == typeid(I).hashOf());
}
class TypeInfo_Struct : TypeInfo
{
override string toString() const { return name; }
+ override size_t toHash() const
+ {
+ return hashOf(this.mangledName);
+ }
+
override bool opEquals(Object o)
{
if (this is o)
return true;
auto s = cast(const TypeInfo_Struct)o;
- return s && this.name == s.name &&
- this.initializer().length == s.initializer().length;
+ return s && this.mangledName == s.mangledName;
}
override size_t getHash(scope const void* p) @trusted pure nothrow const
@@ -1219,23 +1814,45 @@ class TypeInfo_Struct : TypeInfo
(*xpostblit)(p);
}
- string name;
- void[] m_init; // initializer; m_init.ptr == null if 0 initialize
+ string mangledName;
+
+ final @property string name() nothrow const @trusted
+ {
+ import core.demangle : demangleType;
+
+ if (mangledName is null) // e.g., opaque structs
+ return null;
- @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;
+ const key = cast(const void*) this; // faster lookup than TypeInfo_Struct, at the cost of potential duplicates per binary
+ static string[typeof(key)] demangledNamesCache; // per thread
+
+ // not nothrow:
+ //return demangledNamesCache.require(key, cast(string) demangleType(mangledName));
+
+ if (auto pDemangled = key in demangledNamesCache)
+ return *pDemangled;
+
+ const demangled = cast(string) demangleType(mangledName);
+ demangledNamesCache[key] = demangled;
+ return demangled;
+ }
+
+ void[] m_init; // initializer; m_init.ptr == null if 0 initialize
- enum StructFlags : uint
+ @safe pure nothrow
{
- hasPointers = 0x1,
- isDynamicType = 0x2, // built at runtime, needs type info in xdtor
+ 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;
}
- StructFlags m_flags;
- }
union
{
void function(void*) xdtor;
@@ -1261,7 +1878,7 @@ class TypeInfo_Struct : TypeInfo
immutable(void)* m_RTInfo; // data for precise GC
}
-unittest
+@system unittest
{
struct S
{
@@ -1428,12 +2045,7 @@ class TypeInfo_Inout : TypeInfo_Const
}
}
-
-///////////////////////////////////////////////////////////////////////////////
-// ModuleInfo
-///////////////////////////////////////////////////////////////////////////////
-
-
+// Contents of Moduleinfo._flags
enum
{
MIctorstart = 0x1, // we've started constructing it
@@ -1452,31 +2064,35 @@ enum
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;
+ uint _flags; // MIxxxx
uint _index; // index into _moduleinfo_array[]
version (all)
{
deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.")
- void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; }
+ void opAssign(const scope ModuleInfo m) { _flags = m._flags; _index = m._index; }
}
else
{
@disable this();
- @disable this(this) const;
}
const:
- private void* addrOf(int flag) nothrow pure @nogc
+ private void* addrOf(int flag) return nothrow pure @nogc
in
{
assert(flag >= MItlsctor && flag <= MIname);
assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1));
}
- body
+ do
{
import core.stdc.string : strlen;
@@ -1539,42 +2155,74 @@ const:
@property uint flags() nothrow pure @nogc { return _flags; }
+ /************************
+ * Returns:
+ * module constructor for thread locals, `null` if there isn't one
+ */
@property void function() tlsctor() nothrow pure @nogc
{
return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null;
}
+ /************************
+ * Returns:
+ * module destructor for thread locals, `null` if there isn't one
+ */
@property void function() tlsdtor() nothrow pure @nogc
{
return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null;
}
+ /*****************************
+ * Returns:
+ * address of a module's `const(MemberInfo)[] getMembers(string)` function, `null` if there isn't one
+ */
@property void* xgetMembers() nothrow pure @nogc
{
return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null;
}
+ /************************
+ * Returns:
+ * module constructor, `null` if there isn't one
+ */
@property void function() ctor() nothrow pure @nogc
{
return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null;
}
+ /************************
+ * Returns:
+ * module destructor, `null` if there isn't one
+ */
@property void function() dtor() nothrow pure @nogc
{
return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null;
}
+ /************************
+ * Returns:
+ * module order independent constructor, `null` if there isn't one
+ */
@property void function() ictor() nothrow pure @nogc
{
return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null;
}
+ /*************
+ * Returns:
+ * address of function that runs the module's unittests, `null` if there isn't one
+ */
@property void function() unitTest() nothrow pure @nogc
{
return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null;
}
- @property immutable(ModuleInfo*)[] importedModules() nothrow pure @nogc
+ /****************
+ * Returns:
+ * array of pointers to the ModuleInfo's of modules imported by this one
+ */
+ @property immutable(ModuleInfo*)[] importedModules() return nothrow pure @nogc
{
if (flags & MIimportedModules)
{
@@ -1584,7 +2232,11 @@ const:
return null;
}
- @property TypeInfo_Class[] localClasses() nothrow pure @nogc
+ /****************
+ * Returns:
+ * array of TypeInfo_Class references for classes defined in this module
+ */
+ @property TypeInfo_Class[] localClasses() return nothrow pure @nogc
{
if (flags & MIlocalClasses)
{
@@ -1594,16 +2246,16 @@ const:
return null;
}
- @property string name() nothrow pure @nogc
+ /********************
+ * Returns:
+ * name of module, `null` if no name
+ */
+ @property string name() return nothrow pure @nogc
{
- if (true || flags & MIname) // always available for now
- {
- import core.stdc.string : strlen;
+ import core.stdc.string : strlen;
- auto p = cast(immutable char*)addrOf(MIname);
- return p[0 .. strlen(p)];
- }
- // return null;
+ auto p = cast(immutable char*) addrOf(MIname);
+ return p[0 .. strlen(p)];
}
static int opApply(scope int delegate(ModuleInfo*) dg)
@@ -1617,7 +2269,7 @@ const:
}
}
-unittest
+@system unittest
{
ModuleInfo* m1;
foreach (m; ModuleInfo)
@@ -1677,23 +2329,115 @@ class Throwable : Object
* caught $(D Exception) will be chained to the new $(D Throwable) via this
* field.
*/
- Throwable next;
+ private Throwable nextInChain;
- @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ 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
+ * Note:
+ * Marked as `@system` to discourage casual use of it.
+ */
+ @system @nogc final pure nothrow ref uint refcount() return { return _refcount; }
+
+ /**
+ * Loop over the chain of Throwables.
+ */
+ int opApply(scope int delegate(Throwable) dg)
+ {
+ int result = 0;
+ for (Throwable t = this; t; t = t.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 (!e1)
+ return e2;
+ if (!e2)
+ return e1;
+ if (e2.refcount())
+ ++e2.refcount();
+
+ 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.next = next;
+ this.nextInChain = nextInChain;
+ if (nextInChain && nextInChain._refcount)
+ ++nextInChain._refcount;
//this.info = _d_traceContext();
}
- @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null)
+ @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
{
- this(msg, next);
+ this(msg, nextInChain);
this.file = file;
this.line = line;
//this.info = _d_traceContext();
}
+ @trusted nothrow ~this()
+ {
+ if (nextInChain && nextInChain._refcount)
+ _d_delThrowable(nextInChain);
+ }
+
/**
* Overrides $(D Object.toString) and returns the error message.
* Internally this forwards to the $(D toString) overload that
@@ -1714,15 +2458,13 @@ class Throwable : Object
*/
void toString(scope void delegate(in char[]) sink) const
{
- import core.internal.traits : externDFunc;
- alias sizeToTempString = externDFunc!("core.internal.string.unsignedToTempString",
- char[] function(ulong, return char[], uint) @safe pure nothrow @nogc);
+ import core.internal.string : unsignedToTempString;
char[20] tmpBuff = void;
sink(typeid(this).name);
sink("@"); sink(file);
- sink("("); sink(sizeToTempString(line, tmpBuff, 10)); sink(")");
+ sink("("); sink(unsignedToTempString(line, tmpBuff)); sink(")");
if (msg.length)
{
@@ -1744,6 +2486,19 @@ class Throwable : Object
}
}
}
+
+ /**
+ * 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
+ {
+ return this.msg;
+ }
}
@@ -1759,29 +2514,45 @@ class Exception : Throwable
{
/**
- * Creates a new instance of Exception. The next parameter is used
+ * 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 next = null)
+ @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
{
- super(msg, file, line, next);
+ super(msg, file, line, nextInChain);
}
- @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
{
- super(msg, file, line, next);
+ super(msg, file, line, nextInChain);
}
}
-unittest
+///
+@safe unittest
+{
+ bool gotCaught;
+ try
+ {
+ throw new Exception("msg");
+ }
+ catch (Exception e)
+ {
+ gotCaught = true;
+ assert(e.msg == "msg");
+ }
+ assert(gotCaught);
+}
+
+@system unittest
{
{
auto e = new Exception("msg");
assert(e.file == __FILE__);
assert(e.line == __LINE__ - 2);
- assert(e.next is null);
+ assert(e.nextInChain is null);
assert(e.msg == "msg");
}
@@ -1789,7 +2560,7 @@ unittest
auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42);
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
}
@@ -1797,9 +2568,14 @@ unittest
auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
}
+
+ {
+ auto e = new Exception("message");
+ assert(e.message == "message");
+ }
}
@@ -1815,20 +2591,20 @@ unittest
class Error : Throwable
{
/**
- * Creates a new instance of Error. The next parameter is used
+ * 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 next = null)
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
{
- super(msg, next);
+ super(msg, nextInChain);
bypassedException = null;
}
- @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null)
+ @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
{
- super(msg, file, line, next);
+ super(msg, file, line, nextInChain);
bypassedException = null;
}
@@ -1837,13 +2613,29 @@ class Error : Throwable
Throwable bypassedException;
}
-unittest
+///
+@system unittest
+{
+ bool gotCaught;
+ try
+ {
+ throw new Error("msg");
+ }
+ catch (Error e)
+ {
+ gotCaught = true;
+ assert(e.msg == "msg");
+ }
+ assert(gotCaught);
+}
+
+@safe unittest
{
{
auto e = new Error("msg");
assert(e.file is null);
assert(e.line == 0);
- assert(e.next is null);
+ assert(e.nextInChain is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
@@ -1852,7 +2644,7 @@ unittest
auto e = new Error("msg", new Exception("It's an Exception!"));
assert(e.file is null);
assert(e.line == 0);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
@@ -1861,32 +2653,24 @@ unittest
auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
}
-/* 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
-}
-
extern (C)
{
// from druntime/src/rt/aaA.d
private struct AA { void* impl; }
// size_t _aaLen(in AA aa) pure nothrow @nogc;
- private void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, in size_t valsz, in void* pkey) pure nothrow;
- private void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, in size_t valsz, in void* pkey, out bool found) pure nothrow;
+ private void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
+ private void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) pure nothrow;
// inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
- inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz, const TypeInfo tiValueArray) pure nothrow;
- inout(void[]) _aaKeys(inout AA aa, in size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
- void* _aaRehash(AA* paa, in TypeInfo keyti) pure nothrow;
+ inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow;
+ inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
+ void* _aaRehash(AA* paa, const scope TypeInfo keyti) pure nothrow;
void _aaClear(AA aa) pure nothrow;
// alias _dg_t = extern(D) int delegate(void*);
@@ -1902,8 +2686,8 @@ extern (C)
void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
- int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2);
- hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow;
+ int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
+ hash_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
/*
_d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
@@ -1926,17 +2710,44 @@ alias AssociativeArray(Key, Value) = Value[Key];
* Params:
* aa = The associative array.
*/
-void clear(T : Value[Key], Value, Key)(T aa)
+void clear(Value, Key)(Value[Key] aa)
{
_aaClear(*cast(AA *) &aa);
}
/* ditto */
-void clear(T : Value[Key], Value, Key)(T* aa)
+void clear(Value, Key)(Value[Key]* aa)
{
_aaClear(*cast(AA *) aa);
}
+///
+@system unittest
+{
+ auto aa = ["k1": 2];
+ aa.clear;
+ assert("k1" !in aa);
+}
+
+// Issue 20559
+@system unittest
+{
+ static class Foo
+ {
+ int[string] aa;
+ alias aa this;
+ }
+
+ auto v = new Foo();
+ v["Hello World"] = 42;
+ v.clear;
+ assert("Hello World" !in v);
+
+ // Test for T*
+ static assert(!__traits(compiles, (&v).clear));
+ static assert( __traits(compiles, (*(&v)).clear));
+}
+
/***********************************
* Reorganizes the associative array in place so that lookups are more
* efficient.
@@ -2000,15 +2811,16 @@ V[K] dup(T : V[K], K, V)(T aa)
return *cast(V*)pv;
}
- if (auto postblit = _getPostblit!V())
- {
- foreach (k, ref v; aa)
- postblit(duplicateElem(k, v));
- }
- else
+ foreach (k, ref v; aa)
{
- foreach (k, ref v; aa)
+ static if (!__traits(hasPostblit, V))
duplicateElem(k, v);
+ else static if (__traits(isStaticArray, V))
+ _doPostblit(duplicateElem(k, v)[]);
+ else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
+ (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
+ else
+ duplicateElem(k, v).__xpostblit();
}
return result;
@@ -2020,6 +2832,15 @@ V[K] dup(T : V[K], K, V)(T* aa)
return (*aa).dup;
}
+///
+@safe unittest
+{
+ auto aa = ["k1": 2];
+ auto a2 = aa.dup;
+ aa["k2"] = 3;
+ assert("k2" !in a2);
+}
+
// this should never be made public.
private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
{
@@ -2048,10 +2869,9 @@ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
- @property ref front()
+ @property ref front() @trusted
{
- auto p = (() @trusted => cast(substInout!K*) _aaRangeFrontKey(r)) ();
- return *p;
+ return *cast(substInout!K*) _aaRangeFrontKey(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
@@ -2066,6 +2886,17 @@ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
return (*aa).byKey();
}
+///
+@safe unittest
+{
+ auto dict = [1: 0, 2: 0];
+ int sum;
+ foreach (v; dict.byKey)
+ sum += v;
+
+ assert(sum == 3);
+}
+
/***********************************
* Returns a forward range over the values of the associative array.
* Params:
@@ -2083,10 +2914,9 @@ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
- @property ref front()
+ @property ref front() @trusted
{
- auto p = (() @trusted => cast(substInout!V*) _aaRangeFrontValue(r)) ();
- return *p;
+ return *cast(substInout!V*) _aaRangeFrontValue(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
@@ -2101,6 +2931,17 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
return (*aa).byValue();
}
+///
+@safe unittest
+{
+ auto dict = ["k1": 1, "k2": 2];
+ int sum;
+ foreach (v; dict.byValue)
+ sum += v;
+
+ assert(sum == 3);
+}
+
/***********************************
* Returns a forward range over the key value pairs of the associative array.
* Params:
@@ -2127,15 +2968,13 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
private void* keyp;
private void* valp;
- @property ref key() inout
+ @property ref key() inout @trusted
{
- auto p = (() @trusted => cast(substInout!K*) keyp) ();
- return *p;
+ return *cast(substInout!K*) keyp;
}
- @property ref value() inout
+ @property ref value() inout @trusted
{
- auto p = (() @trusted => cast(substInout!V*) valp) ();
- return *p;
+ return *cast(substInout!V*) valp;
}
}
return Pair(_aaRangeFrontKey(r),
@@ -2154,6 +2993,17 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
return (*aa).byKeyValue();
}
+///
+@safe unittest
+{
+ auto dict = ["k1": 1, "k2": 2];
+ int sum;
+ foreach (e; dict.byKeyValue)
+ sum += e.value;
+
+ assert(sum == 3);
+}
+
/***********************************
* Returns a dynamic array, the elements of which are the keys in the
* associative array.
@@ -2169,9 +3019,12 @@ Key[] keys(T : Value[Key], Value, Key)(T aa) @property
alias realAA = aa;
else
const(Value[Key]) realAA = aa;
- auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
- auto res = *cast(Key[]*)&a;
- _doPostblit(res);
+ auto res = () @trusted {
+ auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
+ return *cast(Key[]*)&a;
+ }();
+ static if (__traits(hasPostblit, Key))
+ _doPostblit(res);
return res;
}
@@ -2181,7 +3034,18 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
return (*aa).keys;
}
-@system unittest
+///
+@safe unittest
+{
+ auto aa = [1: "v1", 2: "v2"];
+ int sum;
+ foreach (k; aa.keys)
+ sum += k;
+
+ assert(sum == 3);
+}
+
+@safe unittest
{
static struct S
{
@@ -2194,6 +3058,36 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
assert(s.keys.length == 0);
}
+@safe unittest
+{
+ @safe static struct Key
+ {
+ string str;
+ this(this) @safe {}
+ }
+ string[Key] aa;
+ static assert(__traits(compiles, {
+ void test() @safe {
+ const _ = aa.keys;
+ }
+ }));
+}
+
+@safe unittest
+{
+ static struct Key
+ {
+ string str;
+ this(this) @system {}
+ }
+ string[Key] aa;
+ static assert(!__traits(compiles, {
+ void test() @safe {
+ const _ = aa.keys;
+ }
+ }));
+}
+
/***********************************
* Returns a dynamic array, the elements of which are the values in the
* associative array.
@@ -2209,9 +3103,12 @@ Value[] values(T : Value[Key], Value, Key)(T aa) @property
alias realAA = aa;
else
const(Value[Key]) realAA = aa;
- auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
- auto res = *cast(Value[]*)&a;
- _doPostblit(res);
+ auto res = () @trusted {
+ auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
+ return *cast(Value[]*)&a;
+ }();
+ static if (__traits(hasPostblit, Value))
+ _doPostblit(res);
return res;
}
@@ -2221,7 +3118,18 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property
return (*aa).values;
}
-@system unittest
+///
+@safe unittest
+{
+ auto aa = ["k1": 1, "k2": 2];
+ int sum;
+ foreach (e; aa.values)
+ sum += e;
+
+ assert(sum == 3);
+}
+
+@safe unittest
{
static struct S
{
@@ -2234,6 +3142,36 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property
assert(s.values.length == 0);
}
+@safe unittest
+{
+ @safe static struct Value
+ {
+ string str;
+ this(this) @safe {}
+ }
+ Value[string] aa;
+ static assert(__traits(compiles, {
+ void test() @safe {
+ const _ = aa.values;
+ }
+ }));
+}
+
+@safe unittest
+{
+ static struct Value
+ {
+ string str;
+ this(this) @system {}
+ }
+ Value[string] aa;
+ static assert(!__traits(compiles, {
+ void test() @safe {
+ const _ = aa.values;
+ }
+ }));
+}
+
/***********************************
* Looks up key; if it exists returns corresponding value else evaluates and
* returns defaultValue.
@@ -2256,6 +3194,13 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
return (*aa).get(key, defaultValue);
}
+@safe unittest
+{
+ auto aa = ["k1": 1];
+ assert(aa.get("k1", 0) == 1);
+ assert(aa.get("k2", 0) == 0);
+}
+
/***********************************
* Looks up key; if it exists returns corresponding value else evaluates
* value, adds it to the associative array and returns it.
@@ -2281,48 +3226,38 @@ ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
}
- return found ? *p : (*p = value);
-}
-
-// Constraints for aa update. Delegates, Functions or Functors (classes that
-// provide opCall) are allowed. See unittest for an example.
-private
-{
- template isCreateOperation(C, V)
+ if (found)
+ return *p;
+ else
{
- static if (is(C : V delegate()) || is(C : V function()))
- enum bool isCreateOperation = true;
- else static if (isCreateOperation!(typeof(&C.opCall), V))
- enum bool isCreateOperation = true;
- else
- enum bool isCreateOperation = false;
+ *p = value; // Not `return (*p = value)` since if `=` is overloaded
+ return *p; // this might not return a ref to the left-hand side.
}
+}
- template isUpdateOperation(U, V)
- {
- static if (is(U : V delegate(ref V)) || is(U : V function(ref V)))
- enum bool isUpdateOperation = true;
- else static if (isUpdateOperation!(typeof(&U.opCall), V))
- enum bool isUpdateOperation = true;
- else
- enum bool isUpdateOperation = false;
- }
+///
+@safe unittest
+{
+ auto aa = ["k1": 1];
+ assert(aa.require("k1", 0) == 1);
+ assert(aa.require("k2", 0) == 0);
+ assert(aa["k2"] == 0);
}
// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
/***********************************
- * Looks up key; if it exists applies the update delegate else evaluates the
- * create delegate and adds it to the associative array
+ * Looks up key; if it exists applies the update callable else evaluates the
+ * create callable and adds it to the associative array
* Params:
* aa = The associative array.
* key = The key.
- * create = The delegate to apply on create.
- * update = The delegate to apply on update.
+ * create = The callable to apply on create.
+ * update = The callable to apply on update.
*/
void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
-if (isCreateOperation!(C, V) && isUpdateOperation!(U, V))
+if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
{
bool found;
// if key is @safe-ly copyable, `update` may infer @safe
@@ -2340,10 +3275,35 @@ if (isCreateOperation!(C, V) && isUpdateOperation!(U, V))
if (!found)
*p = create();
else
- *p = update(*p);
+ {
+ static if (is(typeof(update(*p)) == void))
+ update(*p);
+ else
+ *p = update(*p);
+ }
+}
+
+///
+@system unittest
+{
+ auto aa = ["k1": 1];
+
+ aa.update("k1", {
+ return -1; // create (won't be executed)
+ }, (ref int v) {
+ v += 1; // update
+ });
+ assert(aa["k1"] == 2);
+
+ aa.update("k2", {
+ return 0; // create
+ }, (ref int v) {
+ v = -1; // update (won't be executed)
+ });
+ assert(aa["k2"] == 0);
}
-unittest
+@safe unittest
{
static struct S
{
@@ -2373,557 +3333,348 @@ unittest
static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
}
-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();
-}
-
-private void _destructRecurse(E, size_t n)(ref E[n] arr)
-{
- import core.internal.traits : hasElaborateDestructor;
-
- static if (hasElaborateDestructor!E)
- {
- foreach_reverse (ref elem; arr)
- _destructRecurse(elem);
- }
-}
-
-// Public and explicitly undocumented
-void _postblitRecurse(S)(ref S s)
- if (is(S == struct))
-{
- 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();
-}
-
-// Ditto
-void _postblitRecurse(E, size_t n)(ref E[n] arr)
+@safe unittest
{
- import core.internal.traits : hasElaborateCopyConstructor;
-
- static if (hasElaborateCopyConstructor!E)
+ struct S0
{
- size_t i;
- scope(failure)
+ int opCall(ref int v)
{
- for (; i != 0; --i)
- {
- _destructRecurse(arr[i - 1]); // What to do if this throws?
- }
+ return v + 1;
}
-
- for (i = 0; i < arr.length; ++i)
- _postblitRecurse(arr[i]);
}
-}
-
-// Test destruction/postblit order
-@safe nothrow pure unittest
-{
- string[] order;
- struct InnerTop
+ struct S1
{
- ~this() @safe nothrow pure
+ int opCall()()
{
- order ~= "destroy inner top";
+ return -2;
}
- this(this) @safe nothrow pure
+ T opCall(T)(ref T v)
{
- order ~= "copy inner top";
+ return v + 1;
}
}
- struct InnerMiddle {}
+ int[string] a = ["2" : 1];
+ a.update("2", () => -1, S0.init);
+ assert(a["2"] == 2);
+ a.update("0", () => -1, S0.init);
+ assert(a["0"] == -1);
+ a.update("2", S1.init, S1.init);
+ assert(a["2"] == 3);
+ a.update("1", S1.init, S1.init);
+ assert(a["1"] == -2);
+}
- version (none) // https://issues.dlang.org/show_bug.cgi?id=14242
- struct InnerElement
- {
- static char counter = '1';
+@system unittest
+{
+ int[string] aa;
- ~this() @safe nothrow pure
- {
- order ~= "destroy inner element #" ~ counter++;
- }
+ foreach (n; 0 .. 2)
+ aa.update("k1", {
+ return 7;
+ }, (ref int v) {
+ return v + 3;
+ });
+ assert(aa["k1"] == 10);
+}
- this(this) @safe nothrow pure
- {
- order ~= "copy inner element #" ~ counter++;
- }
- }
+version (CoreDdoc)
+{
+ // This lets DDoc produce better documentation.
- struct InnerBottom
- {
- ~this() @safe nothrow pure
- {
- order ~= "destroy inner bottom";
- }
+ /**
+ Calculates the hash value of `arg` with an optional `seed` initial value.
+ The result might not be equal to `typeid(T).getHash(&arg)`.
- this(this) @safe nothrow pure
- {
- order ~= "copy inner bottom";
- }
- }
+ Params:
+ arg = argument to calculate the hash value of
+ seed = optional `seed` value (may be used for hash chaining)
- struct S
+ Return: calculated hash value of `arg`
+ */
+ size_t hashOf(T)(auto ref T arg, size_t seed)
{
- 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"; }
+ static import core.internal.hash;
+ return core.internal.hash.hashOf(arg, seed);
}
-
- string[] destructRecurseOrder;
+ /// ditto
+ size_t hashOf(T)(auto ref T arg)
{
- S s;
- _destructRecurse(s);
- destructRecurseOrder = order;
- order = null;
+ static import core.internal.hash;
+ return core.internal.hash.hashOf(arg);
}
- 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);
+ @safe unittest
+ {
+ auto h1 = "my.string".hashOf;
+ assert(h1 == "my.string".hashOf);
+ }
}
-
-// Test static struct
-nothrow @safe @nogc unittest
+else
{
- static int i = 0;
- static struct S { ~this() nothrow @safe @nogc { i = 42; } }
- S s;
- _destructRecurse(s);
- assert(i == 42);
+ public import core.internal.hash : hashOf;
}
-unittest
+///
+@system unittest
{
- // Bugzilla 14746
- static struct HasDtor
+ class MyObject
{
- ~this() { assert(0); }
+ size_t myMegaHash() const @safe pure nothrow
+ {
+ return 42;
+ }
}
- static struct Owner
+ struct Test
{
- HasDtor* ptr;
- alias ptr this;
+ 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;
+ }
}
+}
- Owner o;
- assert(o.ptr is null);
- destroy(o); // must not reach in HasDtor.__dtor()
+bool _xopEquals(in void*, in void*)
+{
+ throw new Error("TypeInfo.equals is not implemented");
}
-unittest
+bool _xopCmp(in void*, in void*)
{
- // Bugzilla 14746
- static struct HasPostblit
- {
- this(this) { assert(0); }
- }
- static struct Owner
- {
- HasPostblit* ptr;
- alias ptr this;
- }
+ throw new Error("TypeInfo.compare is not implemented");
+}
- Owner o;
- assert(o.ptr is null);
- _postblitRecurse(o); // must not reach in HasPostblit.__postblit()
+/******************************************
+ * Create RTInfo for type T
+ */
+
+template RTInfoImpl(size_t[] pointerBitmap)
+{
+ immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[];
}
-// Test handling of fixed-length arrays
-// Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242
-unittest
+template NoPointersBitmapPayload(size_t N)
{
- string[] order;
+ enum size_t[N] NoPointersBitmapPayload = 0;
+}
- struct S
- {
- char id;
+template RTInfo(T)
+{
+ enum pointerBitmap = __traits(getPointerBitmap, T);
+ static if (pointerBitmap[1 .. $] == NoPointersBitmapPayload!(pointerBitmap.length - 1))
+ enum RTInfo = rtinfoNoPointers;
+ else
+ enum RTInfo = RTInfoImpl!(pointerBitmap).ptr;
+}
- this(this)
- {
- order ~= "copy #" ~ id;
- }
+/**
+* shortcuts for the precise GC, also generated by the compiler
+* used instead of the actual pointer bitmap
+*/
+enum immutable(void)* rtinfoNoPointers = null;
+enum immutable(void)* rtinfoHasPointers = cast(void*)1;
- ~this()
- {
- order ~= "destroy #" ~ id;
- }
- }
+// Helper functions
- string[] destructRecurseOrder;
+private inout(TypeInfo) getElement(return inout TypeInfo value) @trusted pure nothrow
+{
+ TypeInfo element = cast() value;
+ for (;;)
{
- S[3] arr = [S('1'), S('2'), S('3')];
- _destructRecurse(arr);
- destructRecurseOrder = order;
- order = null;
+ if (auto qualified = cast(TypeInfo_Const) element)
+ element = qualified.base;
+ else if (auto redefined = cast(TypeInfo_Enum) element)
+ element = redefined.base;
+ else if (auto staticArray = cast(TypeInfo_StaticArray) element)
+ element = staticArray.value;
+ else if (auto vector = cast(TypeInfo_Vector) element)
+ element = vector.base;
+ else
+ break;
}
- 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);
+ return cast(inout) element;
}
-// Test handling of failed postblit
-// Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242
-/+ nothrow @safe +/ unittest
+private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, const size_t count) @trusted nothrow
{
- static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } }
- static string[] order;
- static struct Inner
- {
- char id;
-
- @safe:
- this(this)
- {
- order ~= "copy inner #" ~ id;
- if (id == '2')
- throw new FailedPostblitException();
- }
+ if (!count)
+ return 0;
- ~this() nothrow
- {
- order ~= "destroy inner #" ~ id;
- }
- }
+ const size_t elementSize = element.tsize;
+ if (!elementSize)
+ return 0;
- static struct Outer
+ static bool hasCustomToHash(const scope TypeInfo value) @trusted pure nothrow
{
- Inner inner1, inner2, inner3;
-
- nothrow @safe:
- this(char first, char second, char third)
- {
- inner1 = Inner(first);
- inner2 = Inner(second);
- inner3 = Inner(third);
- }
+ const element = getElement(value);
- this(this)
- {
- order ~= "copy outer";
- }
+ if (const struct_ = cast(const TypeInfo_Struct) element)
+ return !!struct_.xtoHash;
- ~this()
- {
- order ~= "destroy outer";
- }
+ return cast(const TypeInfo_Array) element
+ || cast(const TypeInfo_AssociativeArray) element
+ || cast(const ClassInfo) element
+ || cast(const TypeInfo_Interface) element;
}
- 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);
+ if (!hasCustomToHash(element))
+ return hashOf(ptr[0 .. elementSize * count]);
- postblitRecurseOrder = order;
- order = null;
+ size_t hash = 0;
+ foreach (size_t i; 0 .. count)
+ hash = hashOf(element.getHash(ptr + i * elementSize), hash);
+ return hash;
+}
- try auto arrCopy = arr;
- catch (FailedPostblitException) {}
- catch (Exception) assert(false);
+/// Provide the .dup array property.
+@property auto dup(T)(T[] a)
+ if (!is(const(T) : T))
+{
+ import core.internal.traits : Unconst;
+ static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~
+ " to "~Unconst!T.stringof~" in dup.");
- assert(postblitRecurseOrder == order);
+ return _dup!(T, Unconst!T)(a);
}
-/++
- Destroys the given object and puts it in an invalid state. It's used to
- _destroy an object so that any cleanup which its destructor or finalizer
- does is done and so that 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))
+///
+@safe unittest
{
- rt_finalize(cast(void*)obj);
+ auto arr = [1, 2];
+ auto arr2 = arr.dup;
+ arr[0] = 0;
+ assert(arr == [0, 2]);
+ assert(arr2 == [1, 2]);
}
/// ditto
-void destroy(T)(T obj) if (is(T == interface))
-{
- destroy(cast(Object)obj);
-}
-
-version (unittest) 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");
- }
+// const overload to support implicit conversion to immutable (unique result, see DIP29)
+@property T[] dup(T)(const(T)[] a)
+ if (is(const(T) : T))
+{
+ return _dup!(const(T), T)(a);
}
-/// ditto
-void destroy(T)(ref T obj) if (is(T == struct))
-{
- _destructRecurse(obj);
- () @trusted {
- auto buf = (cast(ubyte*) &obj)[0 .. T.sizeof];
- auto init = cast(ubyte[])typeid(T).initializer();
- if (init.ptr is null) // null ptr means initialize to 0s
- buf[] = 0;
- else
- buf[] = init[];
- } ();
-}
-version (unittest) 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" );
- }
+/// Provide the .idup array property.
+@property immutable(T)[] idup(T)(T[] a)
+{
+ static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~
+ " to immutable in idup.");
+ return _dup!(T, immutable(T))(a);
}
/// ditto
-void destroy(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct))
+@property immutable(T)[] idup(T:void)(const(T)[] a)
{
- foreach_reverse (ref e; obj[])
- destroy(e);
+ return a.dup;
}
-version (unittest) unittest
+///
+@safe unittest
{
- int[2] a;
- a[0] = 1;
- a[1] = 2;
- destroy(a);
- assert(a == [ 0, 0 ]);
+ char[] arr = ['a', 'b', 'c'];
+ string s = arr.idup;
+ arr[0] = '.';
+ assert(s == "abc");
}
-unittest
+private U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T))
{
- static struct vec2f {
- float[2] values;
- alias values this;
- }
+ if (__ctfe)
+ return _dupCtfe!(T, U)(a);
- vec2f v;
- destroy!vec2f(v);
+ import core.stdc.string : memcpy;
+ auto arr = _d_newarrayU(typeid(T[]), a.length);
+ memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length);
+ return *cast(U[]*) &arr;
}
-unittest
+private U[] _dupCtfe(T, U)(scope T[] a)
{
- // Bugzilla 15009
- static string op;
- static struct S
- {
- int x;
- this(int x) { op ~= "C" ~ cast(char)('0'+x); this.x = x; }
- this(this) { op ~= "P" ~ cast(char)('0'+x); }
- ~this() { op ~= "D" ~ cast(char)('0'+x); }
- }
-
- {
- S[2] a1 = [S(1), S(2)];
- op = "";
- }
- assert(op == "D2D1"); // built-in scope destruction
- {
- S[2] a1 = [S(1), S(2)];
- op = "";
- destroy(a1);
- assert(op == "D2D1"); // consistent with built-in behavior
- }
-
+ static if (is(T : void))
+ assert(0, "Cannot dup a void[] array at compile time.");
+ else
{
- 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);
+ U[] res;
+ foreach (ref e; a)
+ res ~= e;
+ return res;
}
}
-/// ditto
-void destroy(T)(ref T obj)
- if (!is(T == struct) && !is(T == interface) && !is(T == class) && !_isStaticArray!T)
+private U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
{
- obj = T.init;
-}
+ // note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch
+ // has the extra duty to infer _dup `@system` when the copy-constructor is `@system`.
+ if (__ctfe)
+ return _dupCtfe!(T, U)(a);
-template _isStaticArray(T : U[N], U, size_t N)
-{
- enum bool _isStaticArray = true;
-}
+ import core.lifetime: copyEmplace;
+ U[] res = () @trusted {
+ auto arr = cast(U*) _d_newarrayU(typeid(T[]), a.length);
+ size_t i;
+ scope (failure)
+ {
+ import core.internal.lifetime: emplaceInitializer;
+ // Initialize all remaining elements to not destruct garbage
+ foreach (j; i .. a.length)
+ emplaceInitializer(cast() arr[j]);
+ }
+ for (; i < a.length; i++)
+ {
+ copyEmplace(a.ptr[i], arr[i]);
+ }
+ return cast(U[])(arr[0..a.length]);
+ } ();
-template _isStaticArray(T)
-{
- enum bool _isStaticArray = false;
+ return res;
}
-version (unittest) unittest
+// https://issues.dlang.org/show_bug.cgi?id=22107
+@safe unittest
{
- {
- int a = 42;
- destroy(a);
- assert(a == 0);
- }
- {
- float a = 42;
- destroy(a);
- assert(isnan(a));
- }
-}
+ static int i;
+ @safe struct S
+ {
+ this(this) { i++; }
+ }
-version (unittest)
-{
- private bool isnan(float x)
+ void fun(scope S[] values...) @safe
{
- return x != x;
+ values.dup;
}
}
-private
-{
- 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;
-}
+// HACK: This is a lie. `_d_arraysetcapacity` is neither `nothrow` nor `pure`, but this lie is
+// necessary for now to prevent breaking code.
+private 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) 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);
+ return _d_arraysetcapacity(typeid(T[]), 0, cast(void[]*)&arr);
}
+
///
@safe unittest
{
@@ -2948,38 +3699,54 @@ private
}
/**
- * 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).
- */
+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);
+ if (__ctfe)
+ return newcapacity;
+ else
+ return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void[]*)&arr);
}
+
///
-unittest
+@safe unittest
{
//Static array slice: no capacity. Reserve relocates.
int[4] sarray = [1, 2, 3, 4];
int[] slice = sarray[];
auto u = slice.reserve(8);
assert(u >= 8);
- assert(sarray.ptr !is slice.ptr);
+ assert(&sarray[0] !is &slice[0]);
assert(slice.capacity == u);
//Dynamic array slices
int[] a = [1, 2, 3, 4];
a.reserve(8); //prepare a for appending 4 more items
- auto p = a.ptr;
+ auto p = &a[0];
u = a.capacity;
a ~= [5, 6, 7, 8];
- assert(p == a.ptr); //a should not have been reallocated
+ assert(p == &a[0]); //a should not have been reallocated
assert(u == a.capacity); //a should not have been extended
}
+// https://issues.dlang.org/show_bug.cgi?id=12330, reserve() at CTFE time
+@safe unittest
+{
+ int[] foo() {
+ int[] result;
+ auto a = result.reserve = 5;
+ assert(a == 5);
+ return result;
+ }
+ enum r = foo();
+}
+
// Issue 6646: should be possible to use array.reserve from SafeD.
@safe unittest
{
@@ -2987,28 +3754,33 @@ unittest
a.reserve(10);
}
+// HACK: This is a lie. `_d_arrayshrinkfit` is not `nothrow`, but this lie is necessary
+// for now to prevent breaking code.
+private extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow;
+
/**
- * 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
+Assume that it is safe to append to this array. Appends made to this array
+after calling this function may append in place, even if the array was a
+slice of a larger array to begin with.
+
+Use this only when it is certain there are no elements in use beyond the
+array in the memory block. If there are, those elements will be
+overwritten by appending to this array.
+
+Warning: Calling this function, and then using references to data located after the
+given array results in undefined behavior.
+
+Returns:
+ The input is returned.
+*/
+auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system
{
_d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr));
return arr;
}
+
///
-unittest
+@system unittest
{
int[] a = [1, 2, 3, 4];
@@ -3026,7 +3798,7 @@ unittest
}
}
-unittest
+@system unittest
{
int[] arr;
auto newcap = arr.reserve(2000);
@@ -3042,7 +3814,7 @@ unittest
assert(ptr == arr.ptr);
}
-unittest
+@system unittest
{
int[] arr = [1, 2, 3];
void foo(ref int[] i)
@@ -3056,7 +3828,7 @@ unittest
}
// https://issues.dlang.org/show_bug.cgi?id=10574
-unittest
+@system unittest
{
int[] a;
immutable(int[]) b;
@@ -3070,914 +3842,913 @@ unittest
assert(is(typeof(b3) == immutable(int[])));
}
-version (none)
-{
- // enforce() copied from Phobos std.contracts for destroy(), left out until
- // we decide whether to use it.
-
+private extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow;
- T _enforce(T, string file = __FILE__, int line = __LINE__)
- (T value, lazy const(char)[] msg = null)
+private void _doPostblit(T)(T[] arr)
+{
+ // infer static postblit type, run postblit if any
+ static if (__traits(hasPostblit, T))
{
- if (!value) bailOut(file, line, msg);
- return value;
+ static if (__traits(isStaticArray, T) && is(T : E[], E))
+ _doPostblit(cast(E[]) arr);
+ else static if (!is(typeof(arr[0].__xpostblit())) && is(immutable T == immutable U, U))
+ foreach (ref elem; (() @trusted => cast(U[]) arr)())
+ elem.__xpostblit();
+ else
+ foreach (ref elem; arr)
+ elem.__xpostblit();
}
+}
- T _enforce(T, string file = __FILE__, int line = __LINE__)
- (T value, scope void delegate() dg)
- {
- if (!value) dg();
- return value;
- }
+@safe unittest
+{
+ static struct S1 { int* p; }
+ static struct S2 { @disable this(); }
+ static struct S3 { @disable this(this); }
- T _enforce(T)(T value, lazy Exception ex)
+ int dg1() pure nothrow @safe
{
- if (!value) throw ex();
- return value;
+ {
+ char[] m;
+ string i;
+ m = m.dup;
+ i = i.idup;
+ m = i.dup;
+ i = m.idup;
+ }
+ {
+ S1[] m;
+ immutable(S1)[] i;
+ m = m.dup;
+ i = i.idup;
+ static assert(!is(typeof(m.idup)));
+ static assert(!is(typeof(i.dup)));
+ }
+ {
+ S3[] m;
+ immutable(S3)[] i;
+ static assert(!is(typeof(m.dup)));
+ static assert(!is(typeof(i.idup)));
+ }
+ {
+ shared(S1)[] m;
+ m = m.dup;
+ static assert(!is(typeof(m.idup)));
+ }
+ {
+ int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0);
+ }
+ return 1;
}
- private void _bailOut(string file, int line, in char[] msg)
+ int dg2() pure nothrow @safe
{
- char[21] buf;
- throw new Exception(cast(string)(file ~ "(" ~ ulongToString(buf[], line) ~ "): " ~ (msg ? msg : "Enforcement failed")));
+ {
+ S2[] m = [S2.init, S2.init];
+ immutable(S2)[] i = [S2.init, S2.init];
+ m = m.dup;
+ m = i.dup;
+ i = m.idup;
+ i = i.idup;
+ }
+ return 2;
}
-}
-
-/***************************************
- * Helper function used to see if two containers of different
- * types have the same contents in the same sequence.
- */
+ enum a = dg1();
+ enum b = dg2();
+ assert(dg1() == a);
+ assert(dg2() == b);
+}
-bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
+@system unittest
{
- if (a1.length != a2.length)
- return false;
+ static struct Sunpure { this(this) @safe nothrow {} }
+ static struct Sthrow { this(this) @safe pure {} }
+ static struct Sunsafe { this(this) @system pure nothrow {} }
+ static struct Snocopy { @disable this(this); }
- // 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;
+ [].dup!Sunpure;
+ [].dup!Sthrow;
+ cast(void) [].dup!Sunsafe;
+ static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
+ static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
+ static assert(!__traits(compiles, () { [].dup!Snocopy; }));
- for (;idx < length;++idx)
- {
- if (a1[idx] != a2[idx])
- return false;
- }
- return true;
+ [].idup!Sunpure;
+ [].idup!Sthrow;
+ [].idup!Sunsafe;
+ static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
+ static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
+ static assert(!__traits(compiles, () { [].idup!Snocopy; }));
}
-version (D_Ddoc)
+@safe unittest
{
- // This lets DDoc produce better documentation.
+ // test that the copy-constructor is called with .dup
+ static struct ArrElem
+ {
+ int a;
+ this(int a)
+ {
+ this.a = a;
+ }
+ this(ref const ArrElem)
+ {
+ a = 2;
+ }
+ this(ref ArrElem) immutable
+ {
+ a = 3;
+ }
+ }
- /**
- Calculates the hash value of `arg` with an optional `seed` initial value.
- The result might not be equal to `typeid(T).getHash(&arg)`.
+ auto arr = [ArrElem(1), ArrElem(1)];
- Params:
- arg = argument to calculate the hash value of
- seed = optional `seed` value (may be used for hash chaining)
+ ArrElem[] b = arr.dup;
+ assert(b[0].a == 2 && b[1].a == 2);
- Return: calculated hash value of `arg`
- */
- size_t hashOf(T)(auto ref T arg, size_t seed)
- {
- static import core.internal.hash;
- return core.internal.hash.hashOf(arg, seed);
- }
- /// ditto
- size_t hashOf(T)(auto ref T arg)
- {
- static import core.internal.hash;
- return core.internal.hash.hashOf(arg);
- }
-}
-else
-{
- public import core.internal.hash : hashOf;
+ immutable ArrElem[] c = arr.idup;
+ assert(c[0].a == 3 && c[1].a == 3);
}
-unittest
+@system unittest
{
- // Issue # 16654 / 16764
- auto a = [1];
- auto b = a.dup;
- assert(hashOf(a) == hashOf(b));
+ static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} }
+ static struct Sthrow { this(ref const typeof(this)) @safe pure {} }
+ static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} }
+ [].dup!Sunpure;
+ [].dup!Sthrow;
+ cast(void) [].dup!Sunsafe;
+ static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
+ static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
+
+ // for idup to work on structs that have copy constructors, it is necessary
+ // that the struct defines a copy constructor that creates immutable objects
+ static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} }
+ static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} }
+ static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} }
+ [].idup!ISunpure;
+ [].idup!ISthrow;
+ [].idup!ISunsafe;
+ static assert(!__traits(compiles, () pure { [].idup!ISunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; }));
+ static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; }));
}
-bool _xopEquals(in void*, in void*)
+@safe unittest
{
- throw new Error("TypeInfo.equals is not implemented");
+ static int*[] pureFoo() pure { return null; }
+ { char[] s; immutable x = s.dup; }
+ { immutable x = (cast(int*[])null).dup; }
+ { immutable x = pureFoo(); }
+ { immutable x = pureFoo().dup; }
}
-bool _xopCmp(in void*, in void*)
+@safe unittest
{
- throw new Error("TypeInfo.compare is not implemented");
+ auto a = [1, 2, 3];
+ auto b = a.dup;
+ debug(SENTINEL) {} else
+ assert(b.capacity >= 3);
}
-void __ctfeWrite(const string s) @nogc @safe pure nothrow {}
+@system unittest
+{
+ // Bugzilla 12580
+ void[] m = [0];
+ shared(void)[] s = [cast(shared)1];
+ immutable(void)[] i = [cast(immutable)2];
-/******************************************
- * Create RTInfo for type T
- */
+ s = s.dup;
+ static assert(is(typeof(s.dup) == shared(void)[]));
-template RTInfoImpl(size_t[] pointerBitmap)
-{
- immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[];
+ m = i.dup;
+ i = m.dup;
+ i = i.idup;
+ i = m.idup;
+ i = s.idup;
+ i = s.dup;
+ static assert(!__traits(compiles, m = s.dup));
}
-template NoPointersBitmapPayload(size_t N)
+@safe unittest
{
- enum size_t[N] NoPointersBitmapPayload = 0;
-}
+ // Bugzilla 13809
+ static struct S
+ {
+ this(this) {}
+ ~this() {}
+ }
-template RTInfo(T)
-{
- enum pointerBitmap = __traits(getPointerBitmap, T);
- static if (pointerBitmap[1 .. $] == NoPointersBitmapPayload!(pointerBitmap.length - 1))
- enum RTInfo = rtinfoNoPointers;
- else
- enum RTInfo = RTInfoImpl!(pointerBitmap).ptr;
+ S[] arr;
+ auto a = arr.dup;
}
-/**
-* shortcuts for the precise GC, also generated by the compiler
-* used instead of the actual pointer bitmap
-*/
-enum immutable(void)* rtinfoNoPointers = null;
-enum immutable(void)* rtinfoHasPointers = cast(void*)1;
-
-// lhs == rhs lowers to __equals(lhs, rhs) for dynamic arrays
-bool __equals(T1, T2)(T1[] lhs, T2[] rhs)
+@system 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))
+ // Bugzilla 16504
+ static struct S
{
- // 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;
+ __gshared int* gp;
+ int* p;
+ // postblit and hence .dup could escape
+ this(this) { gp = p; }
}
- else static if (__traits(isIntegral, U1))
+
+ int p;
+ scope S[1] arr = [S(&p)];
+ auto a = arr.dup; // dup does escape
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21983
+// dup/idup destroys partially constructed arrays on failure
+@safe unittest
+{
+ static struct SImpl(bool postblit)
{
+ int num;
+ long l = 0xDEADBEEF;
- if (!__ctfe)
+ static if (postblit)
{
- import core.stdc.string : memcmp;
- return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }();
+ this(this)
+ {
+ if (this.num == 3)
+ throw new Exception("");
+ }
}
else
{
- foreach (const u; 0 .. lhs.length)
+ this(scope ref const SImpl other)
{
- if (at(lhs, u) != at(rhs, u))
- return false;
+ if (other.num == 3)
+ throw new Exception("");
+
+ this.num = other.num;
+ this.l = other.l;
}
- return true;
}
- }
- else
- {
- foreach (const u; 0 .. lhs.length)
+
+ ~this() @trusted
{
- 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 (l != 0xDEADBEEF)
{
- 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
- {
- if (at(lhs, u).tupleof != at(rhs, u).tupleof)
- return false;
+ import core.stdc.stdio;
+ printf("Unexpected value: %lld\n", l);
+ fflush(stdout);
+ assert(false);
}
}
-
- return true;
}
-}
-unittest {
- assert(__equals([], []));
- assert(!__equals([1, 2], [1, 2, 3]));
-}
+ alias Postblit = SImpl!true;
+ alias Copy = SImpl!false;
-unittest
-{
- struct A
+ static int test(S)()
{
- int a;
+ S[4] arr = [ S(1), S(2), S(3), S(4) ];
+ try
+ {
+ arr.dup();
+ assert(false);
+ }
+ catch (Exception)
+ {
+ return 1;
+ }
}
- auto arr1 = [A(0), A(2)];
- auto arr2 = [A(0), A(1)];
- auto arr3 = [A(0), A(1)];
+ static assert(test!Postblit());
+ assert(test!Postblit());
- assert(arr1 != arr2);
- assert(arr2 == arr3);
+ static assert(test!Copy());
+ assert(test!Copy());
}
-unittest
+/**
+Destroys the given object and optionally resets to initial state. It's used to
+_destroy an object, calling its destructor or finalizer so it no longer
+references any other objects. It does $(I not) initiate a GC cycle or free
+any GC memory.
+If `initialize` is supplied `false`, the object is considered invalid after
+destruction, and should not be referenced.
+*/
+void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct))
{
- struct A
- {
- int a;
- int b;
-
- bool opEquals(const A other)
- {
- return this.a == other.b && this.b == other.a;
- }
- }
+ import core.internal.destruction : destructRecurse;
- auto arr1 = [A(1, 0), A(0, 1)];
- auto arr2 = [A(1, 0), A(0, 1)];
- auto arr3 = [A(0, 1), A(1, 0)];
+ destructRecurse(obj);
- assert(arr1 != arr2);
- assert(arr2 == arr3);
+ static if (initialize)
+ {
+ import core.internal.lifetime : emplaceInitializer;
+ emplaceInitializer(obj); // emplace T.init
+ }
}
-// Compare class and interface objects for ordering.
-private int __cmp(Obj)(Obj lhs, Obj rhs)
-if (is(Obj : Object))
+@safe unittest
{
- 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);
+ struct A { string s = "A"; }
+ A a = {s: "B"};
+ assert(a.s == "B");
+ a.destroy;
+ assert(a.s == "A");
}
-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))
- {
- import core.internal.string : dstrcmp;
- return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
- }
- else static if (!is(U == T))
+nothrow @safe @nogc unittest
+{
{
- // Reuse another implementation
- return __cmp(cast(U[]) lhs, cast(U[]) rhs);
+ struct A { string s = "A"; }
+ A a;
+ a.s = "asd";
+ destroy!false(a);
+ assert(a.s == "asd");
+ destroy(a);
+ assert(a.s == "A");
}
- else
{
- immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
- foreach (const u; 0 .. len)
+ static int destroyed = 0;
+ struct C
{
- static if (__traits(isFloating, T))
+ string s = "C";
+ ~this() nothrow @safe @nogc
{
- 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;
+ destroyed ++;
}
- 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);
- }
-}
-
-// 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)
- {
- 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
+ struct B
{
- // 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;
+ C c;
+ string s = "B";
+ ~this() nothrow @safe @nogc
+ {
+ destroyed ++;
+ }
}
+ B a;
+ a.s = "asd";
+ a.c.s = "jkl";
+ destroy!false(a);
+ assert(destroyed == 2);
+ assert(a.s == "asd");
+ assert(a.c.s == "jkl" );
+ destroy(a);
+ assert(destroyed == 4);
+ assert(a.s == "B");
+ assert(a.c.s == "C" );
}
- return s1.length < s2.length ? -1 : (s1.length > s2.length);
}
-// integral types
-@safe unittest
+private extern (C) void rt_finalize(void *data, bool det=true) nothrow;
+
+/// ditto
+void destroy(bool initialize = true, T)(T obj) if (is(T == class))
{
- void compareMinMax(T)()
+ static if (__traits(getLinkage, T) == "C++")
{
- T[2] a = [T.max, T.max];
- T[2] b = [T.min, T.min];
+ static if (__traits(hasMember, T, "__xdtor"))
+ obj.__xdtor();
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
+ static if (initialize)
+ {
+ enum classSize = __traits(classInstanceSize, T);
+ (cast(void*)obj)[0 .. classSize] = typeid(T).initializer[];
+ }
}
-
- compareMinMax!int;
- compareMinMax!uint;
- compareMinMax!long;
- compareMinMax!ulong;
- compareMinMax!short;
- compareMinMax!ushort;
- compareMinMax!byte;
- compareMinMax!dchar;
- compareMinMax!wchar;
+ else
+ rt_finalize(cast(void*)obj);
}
-// char types (dstrcmp)
-@safe unittest
+/// ditto
+void destroy(bool initialize = true, T)(T obj) if (is(T == interface))
{
- void compareMinMax(T)()
- {
- T[2] a = [T.max, T.max];
- T[2] b = [T.min, T.min];
-
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
- }
-
- compareMinMax!ubyte;
- compareMinMax!bool;
- compareMinMax!char;
- compareMinMax!(const char);
+ static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface");
- string s1 = "aaaa";
- string s2 = "bbbb";
- assert(__cmp(s2, s1) > 0);
- assert(__cmp(s1, s2) < 0);
+ destroy!initialize(cast(Object)obj);
}
-// fp types
-@safe unittest
+/// Reference type demonstration
+@system unittest
{
- void compareMinMax(T)()
+ class C
{
- 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];
+ struct Agg
+ {
+ static int dtorCount;
- 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 x = 10;
+ ~this() { dtorCount++; }
+ }
- compareMinMax!real;
- compareMinMax!float;
- compareMinMax!double;
- compareMinMax!ireal;
- compareMinMax!ifloat;
- compareMinMax!idouble;
- compareMinMax!creal;
- //compareMinMax!cfloat;
- compareMinMax!cdouble;
+ static int dtorCount;
- // qualifiers
- compareMinMax!(const real);
- compareMinMax!(immutable real);
-}
+ string s = "S";
+ Agg a;
+ ~this() { dtorCount++; }
+ }
-// void[]
-@safe unittest
-{
- void[] a;
- const(void)[] b;
+ 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`
- (() @trusted
+ // check C++ classes work too!
+ extern (C++) class CPP
{
- a = cast(void[]) "bb";
- b = cast(const(void)[]) "aa";
- })();
+ struct Agg
+ {
+ __gshared int dtorCount;
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
-}
+ int x = 10;
+ ~this() { dtorCount++; }
+ }
-// arrays of arrays with mixed modifiers
+ __gshared int dtorCount;
+
+ string s = "S";
+ Agg a;
+ ~this() { dtorCount++; }
+ }
+
+ CPP cpp = new CPP();
+ assert(cpp.dtorCount == 0); // destructor not yet called
+ assert(cpp.s == "S"); // initial state `cpp.s` is `"S"`
+ assert(cpp.a.dtorCount == 0); // destructor not yet called
+ assert(cpp.a.x == 10); // initial state `cpp.a.x` is `10`
+ cpp.s = "T";
+ cpp.a.x = 30;
+ assert(cpp.s == "T"); // `cpp.s` is `"T"`
+ destroy!false(cpp); // destroy without initialization
+ assert(cpp.dtorCount == 1); // `cpp`'s destructor was called
+ assert(cpp.s == "T"); // `cpp.s` is not initialized
+ assert(cpp.a.dtorCount == 1); // `cpp.a`'s destructor was called
+ assert(cpp.a.x == 30); // `cpp.a.x` is not initialized
+ destroy(cpp);
+ assert(cpp.dtorCount == 2); // `cpp`'s destructor was called again
+ assert(cpp.s == "S"); // `cpp.s` is back to its inital state, `"S"`
+ assert(cpp.a.dtorCount == 2); // `cpp.a`'s destructor was called again
+ assert(cpp.a.x == 10); // `cpp.a.x` is back to its inital state, `10`
+}
+
+/// Value type demonstration
@safe unittest
{
- // 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));
+ int i;
+ assert(i == 0); // `i`'s initial state is `0`
+ i = 1;
+ assert(i == 1); // `i` changed to `1`
+ destroy!false(i);
+ assert(i == 1); // `i` was not initialized
+ destroy(i);
+ assert(i == 0); // `i` is back to its initial state `0`
}
-// objects
-@safe unittest
+@system unittest
{
- class C
+ extern(C++)
+ static class C
{
- int i;
- this(int i) { this.i = i; }
-
- override int opCmp(Object c) const @safe
- {
- return i - (cast(C)c).i;
- }
+ void* ptr;
+ this() {}
}
- 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);
+ destroy!false(new C());
+ destroy!true(new C());
}
-// structs
-@safe unittest
+@system unittest
{
- struct C
+ // class with an `alias this`
+ class A
{
- ubyte i;
- this(ubyte i) { this.i = i; }
+ static int dtorCount;
+ ~this()
+ {
+ dtorCount++;
+ }
}
- auto c1 = C(1);
- auto c2 = C(2);
+ class B
+ {
+ A a;
+ alias a this;
+ this()
+ {
+ a = new A;
+ }
+ static int dtorCount;
+ ~this()
+ {
+ dtorCount++;
+ }
+ }
+ auto b = new B;
+ assert(A.dtorCount == 0);
+ assert(B.dtorCount == 0);
+ destroy(b);
+ assert(A.dtorCount == 0);
+ assert(B.dtorCount == 1);
- assert(__cmp([c1, c1][], [c2, c2][]) < 0);
- assert(__cmp([c2, c2], [c1, c1]) > 0);
- assert(__cmp([c2, c2], [c2, c1]) > 0);
+ auto a = new A;
+ destroy(a);
+ assert(A.dtorCount == 1);
}
-// Compiler hook into the runtime implementation of array (vector) operations.
-template _arrayOp(Args...)
+@system unittest
{
- import core.internal.arrayop;
- alias _arrayOp = arrayOp!Args;
-}
-
-// Helper functions
+ 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");
-private inout(TypeInfo) getElement(inout TypeInfo value) @trusted pure nothrow
-{
- TypeInfo element = cast() value;
- for (;;)
+ I i = b;
+ destroy(i);
+ assert(b.s == "A");
+ }
{
- if (auto qualified = cast(TypeInfo_Const) element)
- element = qualified.base;
- else if (auto redefined = cast(TypeInfo_Enum) element)
- element = redefined.base;
- else if (auto staticArray = cast(TypeInfo_StaticArray) element)
- element = staticArray.value;
- else if (auto vector = cast(TypeInfo_Vector) element)
- element = vector.base;
- else
- break;
+ 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 cast(inout) element;
}
-private size_t getArrayHash(in TypeInfo element, in void* ptr, in size_t count) @trusted nothrow
+nothrow @safe @nogc unittest
{
- if (!count)
- return 0;
-
- const size_t elementSize = element.tsize;
- if (!elementSize)
- return 0;
-
- static bool hasCustomToHash(in TypeInfo value) @trusted pure nothrow
{
- const element = getElement(value);
-
- if (const struct_ = cast(const TypeInfo_Struct) element)
- return !!struct_.xtoHash;
-
- return cast(const TypeInfo_Array) element
- || cast(const TypeInfo_AssociativeArray) element
- || cast(const ClassInfo) element
- || cast(const TypeInfo_Interface) element;
+ struct A { string s = "A"; }
+ A a;
+ a.s = "asd";
+ destroy!false(a);
+ assert(a.s == "asd");
+ destroy(a);
+ assert(a.s == "A");
}
+ {
+ static int destroyed = 0;
+ struct C
+ {
+ string s = "C";
+ ~this() nothrow @safe @nogc
+ {
+ destroyed ++;
+ }
+ }
- import core.internal.traits : externDFunc;
- if (!hasCustomToHash(element))
- return hashOf(ptr[0 .. elementSize * count]);
-
- size_t hash = 0;
- foreach (size_t i; 0 .. count)
- hash = hashOf(element.getHash(ptr + i * elementSize), hash);
- return hash;
+ struct B
+ {
+ C c;
+ string s = "B";
+ ~this() nothrow @safe @nogc
+ {
+ destroyed ++;
+ }
+ }
+ B a;
+ a.s = "asd";
+ a.c.s = "jkl";
+ destroy!false(a);
+ assert(destroyed == 2);
+ assert(a.s == "asd");
+ assert(a.c.s == "jkl" );
+ destroy(a);
+ assert(destroyed == 4);
+ assert(a.s == "B");
+ assert(a.c.s == "C" );
+ }
}
-/// Provide the .dup array property.
-@property auto dup(T)(T[] a)
- if (!is(const(T) : T))
+nothrow unittest
{
- import core.internal.traits : Unconst;
- static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~
- " to "~Unconst!T.stringof~" in dup.");
+ // Bugzilla 20049: Test to ensure proper behavior of `nothrow` destructors
+ class C
+ {
+ static int dtorCount = 0;
+ this() nothrow {}
+ ~this() nothrow { dtorCount++; }
+ }
- // wrap unsafe _dup in @trusted to preserve @safe postblit
- static if (__traits(compiles, (T b) @safe { T a = b; }))
- return _trustedDup!(T, Unconst!T)(a);
- else
- return _dup!(T, Unconst!T)(a);
+ auto c = new C;
+ destroy(c);
+ assert(C.dtorCount == 1);
}
/// ditto
-// const overload to support implicit conversion to immutable (unique result, see DIP29)
-@property T[] dup(T)(const(T)[] a)
- if (is(const(T) : T))
+void destroy(bool initialize = true, T)(ref T obj)
+if (__traits(isStaticArray, T))
{
- // wrap unsafe _dup in @trusted to preserve @safe postblit
- static if (__traits(compiles, (T b) @safe { T a = b; }))
- return _trustedDup!(const(T), T)(a);
- else
- return _dup!(const(T), T)(a);
+ foreach_reverse (ref e; obj[])
+ destroy!initialize(e);
}
-
-/// Provide the .idup array property.
-@property immutable(T)[] idup(T)(T[] a)
+@safe unittest
{
- static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~
- " to immutable in idup.");
-
- // wrap unsafe _dup in @trusted to preserve @safe postblit
- static if (__traits(compiles, (T b) @safe { T a = b; }))
- return _trustedDup!(T, immutable(T))(a);
- else
- return _dup!(T, immutable(T))(a);
+ int[2] a;
+ a[0] = 1;
+ a[1] = 2;
+ destroy!false(a);
+ assert(a == [ 1, 2 ]);
+ destroy(a);
+ assert(a == [ 0, 0 ]);
}
-/// ditto
-@property immutable(T)[] idup(T:void)(const(T)[] a)
+@safe unittest
{
- return a.dup;
-}
+ static struct vec2f {
+ float[2] values;
+ alias values this;
+ }
-private U[] _trustedDup(T, U)(T[] a) @trusted
-{
- return _dup!(T, U)(a);
+ vec2f v;
+ destroy!(true, vec2f)(v);
}
-private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit
+@system unittest
{
- if (__ctfe)
+ // Bugzilla 15009
+ static string op;
+ static struct S
{
- static if (is(T : void))
- assert(0, "Cannot dup a void[] array at compile time.");
- else
- {
- U[] res;
- foreach (ref e; a)
- res ~= e;
- return res;
- }
+ 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); }
}
- import core.stdc.string : memcpy;
-
- void[] arr = _d_newarrayU(typeid(T[]), a.length);
- memcpy(arr.ptr, cast(const(void)*)a.ptr, T.sizeof * a.length);
- auto res = *cast(U[]*)&arr;
-
- static if (!is(T : void))
- _doPostblit(res);
- return res;
-}
-
-private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow;
-
-
-/**************
- * Get the postblit for type T.
- * Returns:
- * null if no postblit is necessary
- * function pointer for struct postblits
- * delegate for class postblits
- */
-private auto _getPostblit(T)() @trusted pure nothrow @nogc
-{
- // infer static postblit type, run postblit if any
- static if (is(T == struct))
{
- import core.internal.traits : Unqual;
- // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/...
- alias _PostBlitType = typeof(function (ref T t){ T a = t; });
- return cast(_PostBlitType)typeid(Unqual!T).xpostblit;
+ S[2] a1 = [S(1), S(2)];
+ op = "";
}
- else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit)
+ assert(op == "D2D1"); // built-in scope destruction
{
- alias _PostBlitType = typeof(delegate (ref T t){ T a = t; });
- return cast(_PostBlitType)&typeid(T).postblit;
+ S[2] a1 = [S(1), S(2)];
+ op = "";
+ destroy(a1);
+ assert(op == "D2D1"); // consistent with built-in behavior
}
- else
- return null;
-}
-private void _doPostblit(T)(T[] arr)
-{
- // infer static postblit type, run postblit if any
- if (auto postblit = _getPostblit!T())
{
- foreach (ref elem; arr)
- postblit(elem);
+ 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);
}
}
-unittest
+// https://issues.dlang.org/show_bug.cgi?id=19218
+@system unittest
{
- static struct S1 { int* p; }
- static struct S2 { @disable this(); }
- static struct S3 { @disable this(this); }
-
- int dg1() pure nothrow @safe
+ static struct S
{
- {
- char[] m;
- string i;
- m = m.dup;
- i = i.idup;
- m = i.dup;
- i = m.idup;
- }
- {
- S1[] m;
- immutable(S1)[] i;
- m = m.dup;
- i = i.idup;
- static assert(!is(typeof(m.idup)));
- static assert(!is(typeof(i.dup)));
- }
- {
- S3[] m;
- immutable(S3)[] i;
- static assert(!is(typeof(m.dup)));
- static assert(!is(typeof(i.idup)));
- }
- {
- shared(S1)[] m;
- m = m.dup;
- static assert(!is(typeof(m.idup)));
- }
- {
- int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0);
- }
- return 1;
+ static dtorCount = 0;
+ ~this() { ++dtorCount; }
}
- int dg2() pure nothrow @safe
+ static interface I
{
- {
- S2[] m = [S2.init, S2.init];
- immutable(S2)[] i = [S2.init, S2.init];
- m = m.dup;
- m = i.dup;
- i = m.idup;
- i = i.idup;
- }
- return 2;
+ ref S[3] getArray();
+ alias getArray this;
}
- enum a = dg1();
- enum b = dg2();
- assert(dg1() == a);
- assert(dg2() == b);
-}
+ static class C : I
+ {
+ static dtorCount = 0;
+ ~this() { ++dtorCount; }
-unittest
-{
- static struct Sunpure { this(this) @safe nothrow {} }
- static struct Sthrow { this(this) @safe pure {} }
- static struct Sunsafe { this(this) @system pure nothrow {} }
+ S[3] a;
+ alias a this;
- static assert( __traits(compiles, () { [].dup!Sunpure; }));
- static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
- static assert( __traits(compiles, () { [].dup!Sthrow; }));
- static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
- static assert( __traits(compiles, () { [].dup!Sunsafe; }));
- static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
+ ref S[3] getArray() { return a; }
+ }
- static assert( __traits(compiles, () { [].idup!Sunpure; }));
- static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
- static assert( __traits(compiles, () { [].idup!Sthrow; }));
- static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
- static assert( __traits(compiles, () { [].idup!Sunsafe; }));
- static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
+ C c = new C();
+ destroy(c);
+ assert(S.dtorCount == 3);
+ assert(C.dtorCount == 1);
+
+ I i = new C();
+ destroy(i);
+ assert(S.dtorCount == 6);
+ assert(C.dtorCount == 2);
}
-unittest
+/// ditto
+void destroy(bool initialize = true, T)(ref T obj)
+ if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
{
- static int*[] pureFoo() pure { return null; }
- { char[] s; immutable x = s.dup; }
- { immutable x = (cast(int*[])null).dup; }
- { immutable x = pureFoo(); }
- { immutable x = pureFoo().dup; }
+ static if (initialize)
+ obj = T.init;
}
-unittest
+@safe unittest
{
- auto a = [1, 2, 3];
- auto b = a.dup;
- debug(SENTINEL) {} else
- assert(b.capacity >= 3);
+ {
+ int a = 42;
+ destroy!false(a);
+ assert(a == 42);
+ destroy(a);
+ assert(a == 0);
+ }
+ {
+ float a = 42;
+ destroy!false(a);
+ assert(a == 42);
+ destroy(a);
+ assert(a != a); // isnan
+ }
}
-unittest
+@safe unittest
{
- // Bugzilla 12580
- void[] m = [0];
- shared(void)[] s = [cast(shared)1];
- immutable(void)[] i = [cast(immutable)2];
+ // Bugzilla 14746
+ static struct HasDtor
+ {
+ ~this() { assert(0); }
+ }
+ static struct Owner
+ {
+ HasDtor* ptr;
+ alias ptr this;
+ }
- s = s.dup;
- static assert(is(typeof(s.dup) == shared(void)[]));
+ Owner o;
+ assert(o.ptr is null);
+ destroy(o); // must not reach in HasDtor.__dtor()
+}
- m = i.dup;
- i = m.dup;
- i = i.idup;
- i = m.idup;
- i = s.idup;
- i = s.dup;
- static assert(!__traits(compiles, m = s.dup));
+/* ************************************************************************
+ COMPILER SUPPORT
+The compiler lowers certain expressions to instantiations of the following
+templates. They must be implicitly imported, which is why they are here
+in this file. They must also be `public` as they must be visible from the
+scope in which they are instantiated. They are explicitly undocumented as
+they are only intended to be instantiated by the compiler, not the user.
+**************************************************************************/
+
+public import core.internal.entrypoint : _d_cmain;
+
+public import core.internal.array.appending : _d_arrayappendTImpl;
+public import core.internal.array.appending : _d_arrayappendcTXImpl;
+public import core.internal.array.comparison : __cmp;
+public import core.internal.array.equality : __equals;
+public import core.internal.array.casting: __ArrayCast;
+public import core.internal.array.concatenation : _d_arraycatnTXImpl;
+public import core.internal.array.construction : _d_arrayctor;
+public import core.internal.array.construction : _d_arraysetctor;
+public import core.internal.array.capacity: _d_arraysetlengthTImpl;
+
+public import core.internal.dassert: _d_assert_fail;
+
+public import core.internal.destruction: __ArrayDtor;
+
+public import core.internal.moving: __move_post_blt;
+
+public import core.internal.postblit: __ArrayPostblit;
+
+public import core.internal.switch_: __switch;
+public import core.internal.switch_: __switch_error;
+
+public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable);
+
+// Compare class and interface objects for ordering.
+private int __cmp(Obj)(Obj lhs, Obj rhs)
+if (is(Obj : Object))
+{
+ 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);
}
-unittest
+// objects
+@safe unittest
{
- // Bugzilla 13809
- static struct S
+ class C
{
- this(this) {}
- ~this() {}
+ int i;
+ this(int i) { this.i = i; }
+
+ override int opCmp(Object c) const @safe
+ {
+ return i - (cast(C)c).i;
+ }
}
- S[] arr;
- auto a = arr.dup;
+ 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);
}
-unittest
+// structs
+@safe unittest
{
- // Bugzilla 16504
- static struct S
+ struct C
{
- __gshared int* gp;
- int* p;
- // postblit and hence .dup could escape
- this(this) { gp = p; }
+ ubyte i;
+ this(ubyte i) { this.i = i; }
}
- int p;
- scope arr = [S(&p)];
- auto a = arr.dup; // dup does escape
+ auto c1 = C(1);
+ auto c2 = C(2);
+
+ assert(__cmp([c1, c1][], [c2, c2][]) < 0);
+ assert(__cmp([c2, c2], [c1, c1]) > 0);
+ assert(__cmp([c2, c2], [c2, c1]) > 0);
}
-// compiler frontend lowers dynamic array comparison to this
-bool __ArrayEq(T1, T2)(T1[] a, T2[] b)
+@safe unittest
{
- if (a.length != b.length)
- return false;
- foreach (size_t i; 0 .. a.length)
- {
- if (a[i] != b[i])
- return false;
- }
- return true;
+ auto a = "hello"c;
+
+ assert(a > "hel");
+ assert(a >= "hel");
+ assert(a < "helloo");
+ assert(a <= "helloo");
+ assert(a > "betty");
+ assert(a >= "betty");
+ assert(a == "hello");
+ assert(a <= "hello");
+ assert(a >= "hello");
+ assert(a < "я");
}
-// compiler frontend lowers struct array postblitting to this
-void __ArrayPostblit(T)(T[] a)
+// Used in Exception Handling LSDA tables to 'wrap' C++ type info
+// so it can be distinguished from D TypeInfo
+class __cpp_type_info_ptr
{
- foreach (ref T e; a)
- e.__xpostblit();
+ void* ptr; // opaque pointer to C++ RTTI type info
}
-// compiler frontend lowers dynamic array deconstruction to this
-void __ArrayDtor(T)(T[] a)
+// Compiler hook into the runtime implementation of array (vector) operations.
+template _arrayOp(Args...)
{
- foreach_reverse (ref T e; a)
- e.__xdtor();
+ import core.internal.array.operations;
+ alias _arrayOp = arrayOp!Args;
}
+
+public import core.builtins : __ctfeWrite;