aboutsummaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/rt
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-07-27 10:15:41 -0700
committerIan Lance Taylor <iant@golang.org>2022-07-27 10:15:41 -0700
commit9f62ed218fa656607740b386c0caa03e65dcd283 (patch)
tree6bde49bc5e4c4241266b108e4277baef4b85535d /libphobos/libdruntime/rt
parent71e955da39cea0ebffcfee3432effa622d14ca99 (diff)
parent5eb9f117a361538834b9740d59219911680717d1 (diff)
downloadgcc-9f62ed218fa656607740b386c0caa03e65dcd283.zip
gcc-9f62ed218fa656607740b386c0caa03e65dcd283.tar.gz
gcc-9f62ed218fa656607740b386c0caa03e65dcd283.tar.bz2
Merge from trunk revision 5eb9f117a361538834b9740d59219911680717d1.
Diffstat (limited to 'libphobos/libdruntime/rt')
-rw-r--r--libphobos/libdruntime/rt/aApply.d108
-rw-r--r--libphobos/libdruntime/rt/aApplyR.d71
-rw-r--r--libphobos/libdruntime/rt/aaA.d43
-rw-r--r--libphobos/libdruntime/rt/arrayassign.d155
-rw-r--r--libphobos/libdruntime/rt/critical_.d4
-rw-r--r--libphobos/libdruntime/rt/dmain2.d18
-rw-r--r--libphobos/libdruntime/rt/ehalloc.d45
-rw-r--r--libphobos/libdruntime/rt/lifetime.d438
-rw-r--r--libphobos/libdruntime/rt/minfo.d216
-rw-r--r--libphobos/libdruntime/rt/monitor_.d6
-rw-r--r--libphobos/libdruntime/rt/util/typeinfo.d289
11 files changed, 638 insertions, 755 deletions
diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d
index bea441f..5d5ddb3 100644
--- a/libphobos/libdruntime/rt/aApply.d
+++ b/libphobos/libdruntime/rt/aApply.d
@@ -1,7 +1,5 @@
/**
- * This code handles decoding UTF strings for foreach loops. There are 6
- * combinations of conversions between char, wchar, and dchar, and 2 of each
- * of those.
+ * This code handles decoding UTF strings for foreach loops.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -15,9 +13,64 @@ import core.internal.utf : decode, toUTF8;
/**********************************************/
/* 1 argument versions */
-// dg is D, but _aApplycd() is C
-extern (D) alias int delegate(void *) dg_t;
+/**
+Delegate type corresponding to transformed loop body
+
+The parameter is a pointer to the current `char`, `wchar` or `dchar`
+
+Returns: non-zero when a `break` statement is hit
+*/
+extern (D) alias dg_t = int delegate(void* c);
+
+// Note: dg is extern(D), but _aApplycd() is extern(C)
+
+/**
+Loop over a string while changing the UTF encoding
+
+There are 6 combinations of conversions between `char`, `wchar`, and `dchar`,
+and 2 of each of those.
+
+The naming convention is as follows:
+
+_aApply{c,d,w}{c,d,w}{1,2}
+The first letter corresponds to the input string encoding, and the second letter corresponds to the target character type.
+
+- c = `char`
+- w = `wchar`
+- d = `dchar`
+
+The `1` variant only produces the character, the `2` variant also produces a loop index.
+
+Examples:
+---
+void main()
+{
+ string str;
+ wtring wstr;
+ dstring dstr;
+
+ foreach (dchar c; str) {}
+ // _aApplycd1
+
+ foreach (wchar c; dstr) {}
+ // _aApplydw1
+
+ foreach (i, wchar c; str) {}
+ // _aApplycw2
+
+ foreach (wchar w; wstr) {}
+ // no conversion
+}
+---
+
+Params:
+ aa = input string
+ dg = foreach body transformed into a delegate, similar to `opApply`
+
+Returns:
+ non-zero when the loop was exited through a `break`
+*/
extern (C) int _aApplycd1(in char[] aa, dg_t dg)
{
int result;
@@ -78,8 +131,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplywd1(in wchar[] aa, dg_t dg)
{
int result;
@@ -140,8 +192,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplycw1(in char[] aa, dg_t dg)
{
int result;
@@ -215,8 +266,7 @@ unittest
assert(i == 5);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplywc1(in wchar[] aa, dg_t dg)
{
int result;
@@ -296,8 +346,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplydc1(in dchar[] aa, dg_t dg)
{
int result;
@@ -373,8 +422,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplydw1(in dchar[] aa, dg_t dg)
{
int result;
@@ -446,9 +494,20 @@ unittest
/****************************************************************************/
/* 2 argument versions */
-// dg is D, but _aApplycd2() is C
-extern (D) alias int delegate(void *, void *) dg2_t;
+/**
+Delegate type corresponding to transformed loop body
+
+Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`.
+Returns: non-zero when a `break` statement is hit
+*/
+extern (D) alias dg2_t = int delegate(void* i, void* c);
+
+// Note: dg is extern(D), but _aApplycd2() is extern(C)
+
+/**
+Variants of _aApplyXXX that include a loop index.
+*/
extern (C) int _aApplycd2(in char[] aa, dg2_t dg)
{
int result;
@@ -516,8 +575,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg)
{
int result;
@@ -585,8 +643,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplycw2(in char[] aa, dg2_t dg)
{
int result;
@@ -665,8 +722,7 @@ unittest
assert(i == 5);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg)
{
int result;
@@ -751,8 +807,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg)
{
int result;
@@ -832,8 +887,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg)
{ int result;
diff --git a/libphobos/libdruntime/rt/aApplyR.d b/libphobos/libdruntime/rt/aApplyR.d
index 7f19fa8..ce3bb9e 100644
--- a/libphobos/libdruntime/rt/aApplyR.d
+++ b/libphobos/libdruntime/rt/aApplyR.d
@@ -1,7 +1,5 @@
/**
- * This code handles decoding UTF strings for foreach_reverse loops. There are
- * 6 combinations of conversions between char, wchar, and dchar, and 2 of each
- * of those.
+ * This code handles decoding UTF strings for `foreach_reverse` loops.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -15,9 +13,27 @@ import core.internal.utf;
/**********************************************/
/* 1 argument versions */
-// dg is D, but _aApplyRcd() is C
-extern (D) alias int delegate(void *) dg_t;
+// Note: dg is extern(D), but _aApplyRcd() is extern(C)
+/**
+Delegate type corresponding to transformed loop body
+
+The parameter is a pointer to the current `char`, `wchar` or `dchar`
+
+Returns: non-zero when a `break` statement is hit
+*/
+extern (D) alias dg_t = int delegate(void* c);
+
+/**
+Same as `_aApplyXXX` functions, but for `foreach_reverse`
+
+Params:
+ aa = input string
+ dg = foreach body transformed into a delegate, similar to `opApply`
+
+Returns:
+ non-zero when the loop was exited through a `break`
+*/
extern (C) int _aApplyRcd1(in char[] aa, dg_t dg)
{ int result;
@@ -90,8 +106,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg)
{ int result;
@@ -154,8 +169,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRcw1(in char[] aa, dg_t dg)
{ int result;
@@ -241,8 +255,7 @@ unittest
assert(i == 5);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg)
{ int result;
@@ -326,8 +339,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg)
{ int result;
@@ -405,8 +417,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg)
{ int result;
@@ -477,9 +488,20 @@ unittest
/****************************************************************************/
/* 2 argument versions */
-// dg is D, but _aApplyRcd2() is C
-extern (D) alias int delegate(void *, void *) dg2_t;
+/**
+Delegate type corresponding to transformed loop body
+
+Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`.
+Returns: non-zero when a `break` statement is hit
+*/
+extern (D) alias dg2_t = int delegate(void* i, void* c);
+
+// Note: dg is extern(D), but _aApplyRcd2() is extern(C)
+
+/**
+Variants of _aApplyRXXX that include a loop index.
+*/
extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg)
{ int result;
size_t i;
@@ -555,8 +577,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg)
{ int result;
@@ -621,8 +642,7 @@ unittest
assert(i == 4);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg)
{ int result;
@@ -710,8 +730,7 @@ unittest
assert(i == 5);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg)
{ int result;
@@ -797,8 +816,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg)
{ int result;
@@ -877,8 +895,7 @@ unittest
assert(i == 9);
}
-/*****************************/
-
+/// ditto
extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg)
{ int result;
diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d
index 0c38622..f264b01 100644
--- a/libphobos/libdruntime/rt/aaA.d
+++ b/libphobos/libdruntime/rt/aaA.d
@@ -50,7 +50,7 @@ struct AA
private struct Impl
{
private:
- this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
+ this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow
{
keysz = cast(uint) ti.key.tsize;
valsz = cast(uint) ti.value.tsize;
@@ -125,7 +125,7 @@ private:
}
}
- void grow(scope const TypeInfo keyti)
+ void grow(scope const TypeInfo keyti) pure nothrow
{
// If there are so many deleted entries, that growing would push us
// below the shrink threshold, we just purge deleted entries instead.
@@ -135,7 +135,7 @@ private:
resize(GROW_FAC * dim);
}
- void shrink(scope const TypeInfo keyti)
+ void shrink(scope const TypeInfo keyti) pure nothrow
{
if (dim > INIT_NUM_BUCKETS)
resize(dim / GROW_FAC);
@@ -233,7 +233,7 @@ package void entryDtor(void* p, const TypeInfo_Struct sti)
extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign));
}
-private bool hasDtor(const TypeInfo ti)
+private bool hasDtor(const TypeInfo ti) pure nothrow
{
import rt.lifetime : unqualify;
@@ -246,7 +246,7 @@ private bool hasDtor(const TypeInfo ti)
return false;
}
-private immutable(void)* getRTInfo(const TypeInfo ti)
+private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow
{
// classes are references
const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class);
@@ -254,7 +254,7 @@ private immutable(void)* getRTInfo(const TypeInfo ti)
}
// build type info for Entry with additional key and value fields
-TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti)
+TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) nothrow
{
import rt.lifetime : unqualify;
@@ -319,7 +319,8 @@ TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo va
}
// build appropriate RTInfo at runtime
-immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize)
+immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo,
+ immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) pure nothrow
{
enum bitsPerWord = 8 * size_t.sizeof;
@@ -456,7 +457,7 @@ private size_t mix(size_t h) @safe pure nothrow @nogc
return h;
}
-private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti)
+private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) nothrow
{
immutable hash = keyti.getHash(pkey);
// highest bit is set to distinguish empty/deleted from filled buckets
@@ -485,6 +486,18 @@ pure nothrow @nogc unittest
// API Implementation
//------------------------------------------------------------------------------
+/** Allocate associative array data.
+ * Called for `new SomeAA` expression.
+ * Params:
+ * ti = TypeInfo for the associative array
+ * Returns:
+ * A new associative array.
+ */
+extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti)
+{
+ return new Impl(ti);
+}
+
/// Determine number of entries in associative array.
extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
{
@@ -504,7 +517,7 @@ extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
* If key was not in the aa, a mutable pointer to newly inserted value which
* is set to all zeros
*/
-extern (C) void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti,
+extern (C) void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti,
const size_t valsz, scope const void* pkey)
{
bool found;
@@ -525,7 +538,7 @@ extern (C) void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti,
* If key was not in the aa, a mutable pointer to newly inserted value which
* is set to all zeros
*/
-extern (C) void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti,
+extern (C) void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti,
const size_t valsz, scope const void* pkey, out bool found)
{
// lazily alloc implementation
@@ -736,7 +749,15 @@ extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg)
return 0;
}
-/// Construct an associative array of type ti from keys and value
+/** Construct an associative array of type ti from corresponding keys and values.
+ * Called for an AA literal `[k1:v1, k2:v2]`.
+ * Params:
+ * ti = TypeInfo for the associative array
+ * keys = array of keys
+ * vals = array of values
+ * Returns:
+ * A new associative array opaque pointer, or null if `keys` is empty.
+ */
extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
void[] vals)
{
diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d
index 21d50b0..c9db2fc 100644
--- a/libphobos/libdruntime/rt/arrayassign.d
+++ b/libphobos/libdruntime/rt/arrayassign.d
@@ -19,8 +19,10 @@ private
debug(PRINTF) import core.stdc.stdio;
}
-/**
- * Keep for backward binary compatibility. This function can be removed in the future.
+/*
+ * Superseded array assignment hook. Does not take into account destructors:
+ * https://issues.dlang.org/show_bug.cgi?id=13661
+ * Kept for backward binary compatibility. This function can be removed in the future.
*/
extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to)
{
@@ -40,15 +42,44 @@ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to)
}
/**
- * Does array assignment (not construction) from another
- * lvalue array of the same element type.
- * Handles overlapping copies.
- * Input:
- * ti TypeInfo of the element type.
- * dst Points target memory. Its .length is equal to the element count, not byte length.
- * src Points source memory. Its .length is equal to the element count, not byte length.
- * ptmp Temporary memory for element swapping.
- */
+Does array assignment (not construction) from another array of the same
+element type.
+
+Handles overlapping copies.
+
+The `_d_arrayassign_l` variant assumes the right hand side is an lvalue,
+while `_d_arrayassign_r` assumes it's an rvalue, which means it doesn't have to call copy constructors.
+
+Used for static array assignment with non-POD element types:
+---
+struct S
+{
+ ~this() {} // destructor, so not Plain Old Data
+}
+
+void main()
+{
+ S[3] arr;
+ S[3] lvalue;
+
+ arr = lvalue;
+ // Generates:
+ // S _tmp;
+ // _d_arrayassign_l(typeid(S), (cast(void*) lvalue.ptr)[0..lvalue.length], (cast(void*) arr.ptr)[0..arr.length], &_tmp);
+
+ S[3] getRvalue() {return lvalue;}
+ arr = getRvalue();
+ // Similar, but `_d_arrayassign_r`
+}
+---
+
+Params:
+ ti = `TypeInfo` of the array element type.
+ dst = target memory. Its `.length` is equal to the element count, not byte length.
+ src = source memory. Its `.length` is equal to the element count, not byte length.
+ ptmp = Temporary memory for element swapping, must have capacity of `ti.tsize` bytes.
+Returns: `dst`
+*/
extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp)
{
debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize);
@@ -131,16 +162,7 @@ unittest // Bugzilla 14024
assert(op == "YzXy", op);
}
-/**
- * Does array assignment (not construction) from another
- * rvalue array of the same element type.
- * Input:
- * ti TypeInfo of the element type.
- * dst Points target memory. Its .length is equal to the element count, not byte length.
- * src Points source memory. Its .length is equal to the element count, not byte length.
- * It is always allocated on stack and never overlapping with dst.
- * ptmp Temporary memory for element swapping.
- */
+/// ditto
extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp)
{
debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize);
@@ -163,48 +185,22 @@ extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* pt
}
/**
- * Does array initialization (not assignment) from another
- * array of the same element type.
- * ti is the element type.
- */
-extern (C) void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to)
-{
- debug(PRINTF) printf("_d_arrayctor(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize);
-
-
- auto element_size = ti.tsize;
-
- enforceRawArraysConformable("initialization", element_size, from, to);
-
- size_t i;
- try
- {
- for (i = 0; i < to.length; i++)
- {
- // Copy construction is defined as bit copy followed by postblit.
- memcpy(to.ptr + i * element_size, from.ptr + i * element_size, element_size);
- ti.postblit(to.ptr + i * element_size);
- }
- }
- catch (Throwable o)
- {
- /* Destroy, in reverse order, what we've constructed so far
- */
- while (i--)
- {
- ti.destroy(to.ptr + i * element_size);
- }
-
- throw o;
- }
- return to;
-}
-
-
-/**
- * Do assignment to an array.
- * p[0 .. count] = value;
- */
+Set all elements of an array to a single value.
+
+---
+p[0 .. count] = value;
+---
+
+Takes into account postblits and destructors, for Plain Old Data elements,
+`rt/memset.d` is used.
+
+Params:
+ p = pointer to start of array
+ value = bytes of the element to set. Size is derived from `ti`.
+ count = amount of array elements to set
+ ti = type info of the array element type / `value`
+Returns: `p`
+*/
extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti)
{
void* pstart = p;
@@ -227,36 +223,3 @@ extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti)
free(ptmp);
return pstart;
}
-
-/**
- * Do construction of an array.
- * ti[count] p = value;
- */
-extern (C) void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti)
-{
- void* pstart = p;
- auto element_size = ti.tsize;
-
- try
- {
- foreach (i; 0 .. count)
- {
- // Copy construction is defined as bit copy followed by postblit.
- memcpy(p, value, element_size);
- ti.postblit(p);
- p += element_size;
- }
- }
- catch (Throwable o)
- {
- // Destroy, in reverse order, what we've constructed so far
- while (p > pstart)
- {
- p -= element_size;
- ti.destroy(p);
- }
-
- throw o;
- }
- return pstart;
-}
diff --git a/libphobos/libdruntime/rt/critical_.d b/libphobos/libdruntime/rt/critical_.d
index ae18122..36552a3 100644
--- a/libphobos/libdruntime/rt/critical_.d
+++ b/libphobos/libdruntime/rt/critical_.d
@@ -18,13 +18,13 @@ nothrow:
import rt.monitor_, core.atomic;
-extern (C) void _d_critical_init()
+extern (C) void _d_critical_init() @nogc nothrow
{
initMutex(cast(Mutex*)&gcs.mtx);
head = &gcs;
}
-extern (C) void _d_critical_term()
+extern (C) void _d_critical_term() @nogc nothrow
{
// This function is only ever called by the runtime shutdown code
// and therefore is single threaded so the following cast is fine.
diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d
index 47b67f1..0739b74 100644
--- a/libphobos/libdruntime/rt/dmain2.d
+++ b/libphobos/libdruntime/rt/dmain2.d
@@ -62,22 +62,21 @@ struct UnitTestResult
bool summarize;
}
-extern (C) void _d_monitor_staticctor();
-extern (C) void _d_monitor_staticdtor();
-extern (C) void _d_critical_init();
-extern (C) void _d_critical_term();
+extern (C) void _d_monitor_staticctor() @nogc nothrow;
+extern (C) void _d_monitor_staticdtor() @nogc nothrow;
+extern (C) void _d_critical_init() @nogc nothrow;
+extern (C) void _d_critical_term() @nogc nothrow;
extern (C) void gc_init();
extern (C) void gc_term();
-extern (C) void thread_init() @nogc;
-extern (C) void thread_term() @nogc;
-extern (C) void lifetime_init();
+extern (C) void thread_init() @nogc nothrow;
+extern (C) void thread_term() @nogc nothrow;
extern (C) void rt_moduleCtor();
extern (C) void rt_moduleTlsCtor();
extern (C) void rt_moduleDtor();
extern (C) void rt_moduleTlsDtor();
extern (C) void thread_joinAll();
extern (C) UnitTestResult runModuleUnitTests();
-extern (C) void _d_initMonoTime();
+extern (C) void _d_initMonoTime() @nogc nothrow;
version (CRuntime_Microsoft)
{
@@ -134,7 +133,6 @@ extern (C) int rt_init()
thread_init();
// TODO: fixme - calls GC.addRange -> Initializes GC
initStaticDataGC();
- lifetime_init();
rt_moduleCtor();
rt_moduleTlsCtor();
return 1;
@@ -669,7 +667,7 @@ extern (C) void _d_print_throwable(Throwable t)
void sink(in char[] buf) scope nothrow
{
- fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
+ fwrite(buf.ptr, char.sizeof, buf.length, stderr);
}
formatThrowable(t, &sink);
}
diff --git a/libphobos/libdruntime/rt/ehalloc.d b/libphobos/libdruntime/rt/ehalloc.d
index 1dcd69a..65f92e3 100644
--- a/libphobos/libdruntime/rt/ehalloc.d
+++ b/libphobos/libdruntime/rt/ehalloc.d
@@ -18,51 +18,6 @@ debug(PRINTF)
import core.stdc.stdio;
}
-/**********************************************
- * Allocate an exception of type `ci` from the exception pool.
- * It has the same interface as `rt.lifetime._d_newclass()`.
- * The class type must be Throwable or derived from it,
- * and cannot be a COM or C++ class. The compiler must enforce
- * this.
- * Returns:
- * default initialized instance of the type
- */
-
-extern (C) Throwable _d_newThrowable(const TypeInfo_Class ci)
-{
- debug(PRINTF) printf("_d_newThrowable(ci = %p, %s)\n", ci, cast(char *)ci.name);
-
- assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass));
- assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass));
-
- import core.stdc.stdlib : malloc;
- auto init = ci.initializer;
- void* p = malloc(init.length);
- if (!p)
- {
- import core.exception : onOutOfMemoryError;
- onOutOfMemoryError();
- }
-
- debug(PRINTF) printf(" p = %p\n", p);
-
- // initialize it
- p[0 .. init.length] = init[];
-
- if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers))
- {
- // Inform the GC about the pointers in the object instance
- import core.memory : GC;
-
- GC.addRange(p, init.length, ci);
- }
-
- debug(PRINTF) printf("initialization done\n");
- Throwable t = cast(Throwable)p;
- t.refcount() = 1;
- return t;
-}
-
/********************************************
* Delete exception instance `t` from the exception pool.
diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d
index 1f7a81d..026001f 100644
--- a/libphobos/libdruntime/rt/lifetime.d
+++ b/libphobos/libdruntime/rt/lifetime.d
@@ -40,22 +40,47 @@ private
}
}
-extern (C) void lifetime_init()
+// Now-removed symbol, kept around for ABI
+// Some programs are dynamically linked, so best to err on the side of keeping symbols around for a while (especially extern(C) ones)
+// https://github.com/dlang/druntime/pull/3361
+deprecated extern (C) void lifetime_init()
{
- // this is run before static ctors, so it is safe to modify immutables
}
/**
- *
- */
+Allocate memory using the garbage collector
+
+DMD uses this to allocate closures:
+---
+void f(byte[24] x)
+{
+ return () => x; // `x` is on stack, must be moved to heap to keep it alive
+}
+---
+
+Params:
+ sz = number of bytes to allocate
+
+Returns: pointer to `sz` bytes of free, uninitialized memory, managed by the GC.
+*/
extern (C) void* _d_allocmemory(size_t sz) @weak
{
return GC.malloc(sz);
}
/**
- *
- */
+Create a new class instance.
+
+Allocates memory and sets fields to their initial value, but does not call a constructor.
+
+---
+new Object() // _d_newclass(typeid(Object))
+---
+Params:
+ ci = `TypeInfo_Class` object, to provide instance size and initial bytes to copy
+
+Returns: newly created object
+*/
extern (C) Object _d_newclass(const ClassInfo ci) @weak
{
import core.stdc.stdlib;
@@ -163,23 +188,6 @@ extern (C) void _d_delclass(Object* p) @weak
}
}
-/**
- * This is called for a delete statement where the value
- * being deleted is a pointer to a struct with a destructor
- * but doesn't have an overloaded delete operator.
- */
-extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) @weak
-{
- if (*p)
- {
- debug(PRINTF) printf("_d_delstruct(%p, %p)\n", *p, cast(void*)inf);
-
- inf.destroy(*p);
- GC.free(*p);
- *p = null;
- }
-}
-
// strip const/immutable/shared/inout from type info
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
{
@@ -367,7 +375,7 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, co
/**
get the allocation size of the array for the given block (without padding or type info)
*/
-size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
+private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
{
if (info.size <= 256)
return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD);
@@ -381,7 +389,7 @@ size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
/**
get the start of the array for the given block
*/
-void *__arrayStart(return scope BlkInfo info) nothrow pure
+private void *__arrayStart(return scope BlkInfo info) nothrow pure
{
return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0);
}
@@ -391,7 +399,7 @@ void *__arrayStart(return scope BlkInfo info) nothrow pure
NOT included in the passed in size. Therefore, do NOT call this function
with the size of an allocated block.
*/
-size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
+private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
{
return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
}
@@ -416,7 +424,7 @@ private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) n
allocate an array memory block by applying the proper padding and
assigning block attributes if not inherited from the existing block
*/
-BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
+private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
{
import core.checkedint;
@@ -438,7 +446,7 @@ BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tin
return bi;
}
-BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
+private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
{
import core.checkedint;
@@ -461,7 +469,7 @@ BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti,
/**
cache for the lookup of the block info
*/
-enum N_CACHE_BLOCKS=8;
+private enum N_CACHE_BLOCKS=8;
// note this is TLS, so no need to sync.
BlkInfo *__blkcache_storage;
@@ -659,10 +667,15 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
}
/**
- * Shrink the "allocated" length of an array to be the exact size of the array.
- * It doesn't matter what the current allocated length of the array is, the
- * user is telling the runtime that he knows what he is doing.
- */
+Shrink the "allocated" length of an array to be the exact size of the array.
+
+It doesn't matter what the current allocated length of the array is, the
+user is telling the runtime that he knows what he is doing.
+
+Params:
+ ti = `TypeInfo` of array type
+ arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type
+*/
extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/
{
// note, we do not care about shared. We are setting the length no matter
@@ -705,7 +718,7 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/
}
}
-package bool hasPostblit(in TypeInfo ti)
+package bool hasPostblit(in TypeInfo ti) nothrow pure
{
return (&ti.postblit).funcptr !is &TypeInfo.postblit;
}
@@ -741,12 +754,21 @@ void __doPostblit(void *ptr, size_t len, const TypeInfo ti)
/**
- * set the array capacity. If the array capacity isn't currently large enough
- * to hold the requested capacity (in number of elements), then the array is
- * resized/reallocated to the appropriate size. Pass in a requested capacity
- * of 0 to get the current capacity. Returns the number of elements that can
- * actually be stored once the resizing is done.
- */
+Set the array capacity.
+
+If the array capacity isn't currently large enough
+to hold the requested capacity (in number of elements), then the array is
+resized/reallocated to the appropriate size.
+
+Pass in a requested capacity of 0 to get the current capacity.
+
+Params:
+ ti = type info of element type
+ newcapacity = requested new capacity
+ p = pointer to array to set. Its `length` is left unchanged.
+
+Returns: the number of elements that can actually be stored once the resizing is done
+*/
extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak
in
{
@@ -917,9 +939,18 @@ Lcontinue:
}
/**
- * Allocate a new uninitialized array of length elements.
- * ti is the type of the resulting array, or pointer to element.
- */
+Allocate an array with the garbage collector.
+
+Has three variants:
+- `_d_newarrayU` leave elements uninitialized
+- `_d_newarrayT` initializes to 0 (e.g `new int[]`)
+- `_d_newarrayiT` initializes based on initializer retrieved from TypeInfo (e.g `new float[]`)
+
+Params:
+ ti = the type of the resulting array, (may also be the corresponding `array.ptr` type)
+ length = `.length` of resulting array
+Returns: newly allocated array
+*/
extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak
{
import core.exception : onOutOfMemoryError;
@@ -976,11 +1007,7 @@ Lcontinue:
return arrstart[0..length];
}
-/**
- * Allocate a new array of length elements.
- * ti is the type of the resulting array, or pointer to element.
- * (For when the array is initialized to 0)
- */
+/// ditto
extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak
{
import core.stdc.string;
@@ -993,9 +1020,7 @@ extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @w
return result;
}
-/**
- * For when the array has a non-zero initializer.
- */
+/// ditto
extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak
{
import core.internal.traits : AliasSeq;
@@ -1011,8 +1036,12 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @
foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
{
case T.sizeof:
- (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr;
- return result;
+ if (tinext.talign % T.alignof == 0)
+ {
+ (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr;
+ return result;
+ }
+ goto default;
}
default:
@@ -1027,10 +1056,10 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @
}
-/**
- *
+/*
+ * Helper for creating multi-dimensional arrays
*/
-void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
+private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
{
debug(PRINTF) printf("_d_newarrayOpT(ndims = %d)\n", dims.length);
if (dims.length == 0)
@@ -1069,8 +1098,30 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
/**
- *
- */
+Create a new multi-dimensional array
+
+Has two variants:
+- `_d_newarraymTX` which initializes to 0
+- `_d_newarraymiTX` which initializes elements based on `TypeInfo`
+
+---
+void main()
+{
+ new int[][](10, 20);
+ // _d_newarraymTX(typeid(float), [10, 20]);
+
+ new float[][][](10, 20, 30);
+ // _d_newarraymiTX(typeid(float), [10, 20, 30]);
+}
+---
+
+Params:
+ ti = `TypeInfo` of the array type
+ dims = array length values for each dimension
+
+Returns:
+ newly allocated array
+*/
extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak
{
debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length);
@@ -1083,10 +1134,7 @@ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak
}
}
-
-/**
- *
- */
+/// ditto
extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak
{
debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length);
@@ -1100,9 +1148,31 @@ extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak
}
/**
- * Allocate an uninitialized non-array item.
- * This is an optimization to avoid things needed for arrays like the __arrayPad(size).
- */
+Allocate an uninitialized non-array item.
+
+This is an optimization to avoid things needed for arrays like the __arrayPad(size).
+
+- `_d_newitemU` leaves the item uninitialized
+- `_d_newitemT` zero initializes the item
+- `_d_newitemiT` uses a non-zero initializer from `TypeInfo`
+
+Used to allocate struct instances on the heap.
+---
+struct Sz {int x = 0;}
+struct Si {int x = 3;}
+
+void main()
+{
+ new Sz(); // _d_newitemT(typeid(Sz))
+ new Si(); // _d_newitemiT(typeid(Si))
+}
+---
+
+Params:
+ _ti = `TypeInfo` of item to allocate
+Returns:
+ newly allocated item
+*/
extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
{
auto ti = unqualify(_ti);
@@ -1118,14 +1188,15 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
if (tiSize)
{
- *cast(TypeInfo*)(p + itemSize) = null; // the GC might not have cleared this area
+ // the GC might not have cleared the padding area in the block
+ *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null;
*cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti;
}
return p;
}
-/// Same as above, zero initializes the item.
+/// ditto
extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
{
import core.stdc.string;
@@ -1145,15 +1216,6 @@ extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak
return p;
}
-/**
- *
- */
-struct Array
-{
- size_t length;
- byte* data;
-}
-
debug(PRINTF)
{
extern(C) void printArrayCache()
@@ -1436,6 +1498,7 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true)
}
}
+/// Backwards compatibility
extern (C) void rt_finalize(void* p, bool det = true) nothrow
{
rt_finalize2(p, det, true);
@@ -1454,8 +1517,29 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow
/**
- * Resize dynamic arrays with 0 initializers.
- */
+Resize a dynamic array by setting the `.length` property
+
+Newly created elements are initialized to their default value.
+
+Has two variants:
+- `_d_arraysetlengthT` for arrays with elements that initialize to 0
+- `_d_arraysetlengthiT` for non-zero initializers retrieved from `TypeInfo`
+
+---
+void main()
+{
+ int[] a = [1, 2];
+ a.length = 3; // gets lowered to `_d_arraysetlengthT(typeid(int[]), 3, &a)`
+}
+---
+
+Params:
+ ti = `TypeInfo` of array
+ newlength = new value for the array's `.length`
+ p = pointer to array to update the `.length` of.
+ While it's cast to `void[]`, its `.length` is still treated as element length.
+Returns: `*p` after being updated
+*/
extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak
in
{
@@ -1649,15 +1733,7 @@ do
return *p;
}
-
-/**
- * Resize arrays for non-zero initializers.
- * p pointer to array lvalue to be updated
- * newlength new .length property of array
- * sizeelem size of each element of array
- * initsize size of initializer
- * ... initializer
- */
+/// ditto
extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak
in
{
@@ -1867,27 +1943,33 @@ do
return *p;
}
-/**
- * Append y[] to array x[]
- */
-extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) @weak
-{
- import core.stdc.string;
- auto length = x.length;
- auto tinext = unqualify(ti.next);
- auto sizeelem = tinext.tsize; // array element size
- _d_arrayappendcTX(ti, x, y.length);
- memcpy(x.ptr + length * sizeelem, y.ptr, y.length * sizeelem);
-
- // do postblit
- __doPostblit(x.ptr + length * sizeelem, y.length * sizeelem, tinext);
- return x;
-}
-
/**
- *
- */
+Given an array of length `size` that needs to be expanded to `newlength`,
+compute a new capacity.
+
+Better version by Dave Fladebo:
+This uses an inverse logorithmic algorithm to pre-allocate a bit more
+space for larger arrays.
+- Arrays smaller than PAGESIZE bytes are left as-is, so for the most
+common cases, memory allocation is 1 to 1. The small overhead added
+doesn't affect small array perf. (it's virtually the same as
+current).
+- Larger arrays have some space pre-allocated.
+- As the arrays grow, the relative pre-allocated space shrinks.
+- The logorithmic algorithm allocates relatively more space for
+mid-size arrays, making it very fast for medium arrays (for
+mid-to-large arrays, this turns out to be quite a bit faster than the
+equivalent realloc() code in C, on Linux at least. Small arrays are
+just as fast as GCC).
+- Perhaps most importantly, overall memory usage and stress on the GC
+is decreased significantly for demanding environments.
+
+Params:
+ newlength = new `.length`
+ size = old `.length`
+Returns: new capacity for array
+*/
size_t newCapacity(size_t newlength, size_t size)
{
version (none)
@@ -1896,24 +1978,6 @@ size_t newCapacity(size_t newlength, size_t size)
}
else
{
- /*
- * Better version by Dave Fladebo:
- * This uses an inverse logorithmic algorithm to pre-allocate a bit more
- * space for larger arrays.
- * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most
- * common cases, memory allocation is 1 to 1. The small overhead added
- * doesn't affect small array perf. (it's virtually the same as
- * current).
- * - Larger arrays have some space pre-allocated.
- * - As the arrays grow, the relative pre-allocated space shrinks.
- * - The logorithmic algorithm allocates relatively more space for
- * mid-size arrays, making it very fast for medium arrays (for
- * mid-to-large arrays, this turns out to be quite a bit faster than the
- * equivalent realloc() code in C, on Linux at least. Small arrays are
- * just as fast as GCC).
- * - Perhaps most importantly, overall memory usage and stress on the GC
- * is decreased significantly for demanding environments.
- */
size_t newcap = newlength * size;
size_t newext = 0;
@@ -1967,10 +2031,17 @@ size_t newCapacity(size_t newlength, size_t size)
}
-/**************************************
- * Extend an array by n elements.
- * Caller must initialize those elements.
- */
+/**
+Extend an array by n elements.
+
+Caller must initialize those elements.
+
+Params:
+ ti = type info of array type (not element type)
+ px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated.
+ n = number of elements to append
+Returns: `px` after being appended to
+*/
extern (C)
byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
{
@@ -2074,8 +2145,21 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n
/**
- * Append dchar to char[]
- */
+Append `dchar` to `char[]`, converting UTF-32 to UTF-8
+
+---
+void main()
+{
+ char[] s;
+ s ~= 'α';
+}
+---
+
+Params:
+ x = array to append to cast to `byte[]`. Will be modified.
+ c = `dchar` to append
+Returns: updated `x` cast to `void[]`
+*/
extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak
{
// c could encode into from 1 to 4 characters
@@ -2121,7 +2205,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak
// Hack because _d_arrayappendT takes `x` as a reference
auto xx = cast(shared(char)[])x;
- object._d_arrayappendTImpl!(shared(char)[])._d_arrayappendT(xx, cast(shared(char)[])appendthis);
+ object._d_arrayappendT(xx, cast(shared(char)[])appendthis);
x = cast(byte[])xx;
return x;
}
@@ -2156,8 +2240,23 @@ unittest
/**
- * Append dchar to wchar[]
- */
+Append `dchar` to `wchar[]`, converting UTF-32 to UTF-16
+
+---
+void main()
+{
+ dchar x;
+ wchar[] s;
+ s ~= 'α';
+}
+---
+
+Params:
+ x = array to append to cast to `byte[]`. Will be modified.
+ c = `dchar` to append
+
+Returns: updated `x` cast to `void[]`
+*/
extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
{
// c could encode into from 1 to 2 w characters
@@ -2182,15 +2281,31 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
//
auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length];
- object._d_arrayappendTImpl!(shared(wchar)[])._d_arrayappendT(xx, cast(shared(wchar)[])appendthis);
+ object._d_arrayappendT(xx, cast(shared(wchar)[])appendthis);
x = (cast(byte*)xx.ptr)[0 .. xx.length];
return x;
}
/**
- *
- */
+Concatenate two arrays into a new array
+
+---
+void main()
+{
+ int[] x = [10, 20, 30];
+ int[] y = [40, 50];
+ int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]);
+}
+---
+
+Params:
+ ti = type that the two arrays share
+ x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
+ y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
+Returns:
+ resulting concatenated array, with `.length` equal to new element length despite `byte` type
+*/
extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak
out (result)
{
@@ -2255,8 +2370,27 @@ do
/**
- *
- */
+Concatenate multiple arrays at once
+
+This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance.
+
+```
+void main()
+{
+ int[] a, b, c;
+ int[] res = a ~ b ~ c;
+ // _d_arraycatnTX(typeid(int[]),
+ // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]);
+}
+```
+
+Params:
+ ti = type of arrays to concatenate and resulting array
+ arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same
+
+Returns:
+ newly created concatenated array, `.length` equal to the total element length despite `void` type
+*/
extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
{
import core.stdc.string;
@@ -2295,8 +2429,27 @@ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
/**
- * Allocate the array, rely on the caller to do the initialization of the array.
- */
+Allocate an array literal
+
+Rely on the caller to do the initialization of the array.
+
+---
+int[] getArr()
+{
+ return [10, 20];
+ // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2);
+ // res[0] = 10;
+ // res[1] = 20;
+ // return res[0..2];
+}
+---
+
+Params:
+ ti = `TypeInfo` of resulting array type
+ length = `.length` of array literal
+
+Returns: pointer to allocated array
+*/
extern (C)
void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak
{
@@ -2602,11 +2755,6 @@ deprecated unittest
}
dtorCount = 0;
- S1* s1 = new S1;
- _d_delstruct(cast(void**)&s1, typeid(typeof(*s1))); // delete s1;
- assert(dtorCount == 1);
-
- dtorCount = 0;
S1[] arr1 = new S1[7];
_d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1;
assert(dtorCount == 7);
diff --git a/libphobos/libdruntime/rt/minfo.d b/libphobos/libdruntime/rt/minfo.d
index 0d5cd22..7489150 100644
--- a/libphobos/libdruntime/rt/minfo.d
+++ b/libphobos/libdruntime/rt/minfo.d
@@ -58,7 +58,7 @@ struct ModuleGroup
// target modules are involved in a cycle.
//
// The return value is malloc'd using C, so it must be freed after use.
- private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges)
+ private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges) nothrow
{
import core.bitop : bt, btc, bts;
@@ -109,7 +109,7 @@ struct ModuleGroup
// release mode.
if (distance[target] != curdist)
{
- throw new Error("internal error printing module cycle");
+ assert(0, "internal error printing module cycle");
}
// determine the path. This is tricky, because we have to
@@ -162,14 +162,13 @@ struct ModuleGroup
* Throws:
* Exception if it fails.
*/
- void sortCtors(string cycleHandling)
+ void sortCtors(string cycleHandling) nothrow
{
import core.bitop : bts, btr, bt, BitRange;
import core.internal.container.hashtab;
enum OnCycle
{
- deprecate,
abort,
print,
ignore
@@ -180,7 +179,9 @@ struct ModuleGroup
switch (cycleHandling) with(OnCycle)
{
case "deprecate":
- onCycle = deprecate;
+ import core.stdc.stdio : fprintf, stderr;
+ // Option deprecated in 2.101, remove in 2.111
+ fprintf(stderr, "`--DRT-oncycle=deprecate` is no longer supported, using `abort` instead\n");
break;
case "abort":
onCycle = abort;
@@ -196,7 +197,7 @@ struct ModuleGroup
break;
default:
// invalid cycle handling option.
- throw new Error("DRT invalid cycle handling option: " ~ cycleHandling);
+ assert(0, "DRT invalid cycle handling option: " ~ cycleHandling);
}
debug (printModuleDependencies)
@@ -280,7 +281,7 @@ struct ModuleGroup
.free(edges.ptr);
}
- void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) sink)
+ void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) nothrow sink)
{
version (Windows)
enum EOL = "\r\n";
@@ -312,7 +313,7 @@ struct ModuleGroup
//
// If a cycle is detected, returns the index of the module that completes the cycle.
// Returns: true for success, false for a deprecated cycle error
- bool findDeps(size_t idx, size_t* reachable)
+ bool findDeps(size_t idx, size_t* reachable) nothrow
{
static struct stackFrame
{
@@ -356,14 +357,6 @@ struct ModuleGroup
// was already started, this is a cycle.
final switch (onCycle) with(OnCycle)
{
- case deprecate:
- // check with old algorithm
- if (sortCtorsOld(edges))
- {
- // unwind to print deprecation message.
- return false; // deprecated cycle error
- }
- goto case abort; // fall through
case abort:
string errmsg = "";
@@ -418,7 +411,7 @@ struct ModuleGroup
// ctor/dtors that must be dealt with. It recurses only when it finds
// dependencies that also have static ctor/dtors.
// Returns: true for success, false for a deprecated cycle error
- bool processMod(size_t curidx)
+ bool processMod(size_t curidx) nothrow
{
immutable ModuleInfo* current = _modules[curidx];
@@ -461,7 +454,7 @@ struct ModuleGroup
}
// returns `false` if deprecated cycle error otherwise set `result`.
- bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result)
+ bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) nothrow
{
clearFlags(relevant);
clearFlags(ctorstart);
@@ -533,193 +526,6 @@ struct ModuleGroup
sortCtors(rt_configOption("oncycle"));
}
- /******************************
- * This is the old ctor sorting algorithm that does not find all cycles.
- *
- * It is here to allow the deprecated behavior from the original algorithm
- * until people have fixed their code.
- *
- * If no cycles are found, the _ctors and _tlsctors are replaced with the
- * ones generated by this algorithm to preserve the old incorrect ordering
- * behavior.
- *
- * Params:
- * edges = The module edges as found in the `importedModules` member of
- * each ModuleInfo. Generated in sortCtors.
- * Returns:
- * true if no cycle is found, false if one was.
- */
- bool sortCtorsOld(int[][] edges)
- {
- immutable len = edges.length;
- assert(len == _modules.length);
-
- static struct StackRec
- {
- @property int mod()
- {
- return _mods[_idx];
- }
-
- int[] _mods;
- size_t _idx;
- }
-
- auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len];
- // TODO: reuse GCBits by moving it to core.internal.container
- immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
- auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof);
- auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof);
- int[] initialEdges = (cast(int *)malloc(int.sizeof * len))[0 .. len];
- if (!stack.ptr || ctorstart is null || ctordone is null || !initialEdges.ptr)
- assert(0);
- scope (exit)
- {
- .free(stack.ptr);
- .free(ctorstart);
- .free(ctordone);
- .free(initialEdges.ptr);
- }
-
- // initialize the initial edges
- foreach (i, ref v; initialEdges)
- v = cast(int)i;
-
- bool sort(ref immutable(ModuleInfo)*[] ctors, uint mask)
- {
- import core.bitop;
-
- ctors = (cast(immutable(ModuleInfo)**).malloc(len * size_t.sizeof))[0 .. len];
- if (!ctors.ptr)
- assert(0);
-
- // clean flags
- memset(ctorstart, 0, nwords * size_t.sizeof);
- memset(ctordone, 0, nwords * size_t.sizeof);
- size_t stackidx = 0;
- size_t cidx;
-
- int[] mods = initialEdges;
-
- size_t idx;
- while (true)
- {
- while (idx < mods.length)
- {
- auto m = mods[idx];
-
- if (bt(ctordone, m))
- {
- // this module has already been processed, skip
- ++idx;
- continue;
- }
- else if (bt(ctorstart, m))
- {
- /* Trace back to the begin of the cycle.
- */
- bool ctorInCycle;
- size_t start = stackidx;
- while (start--)
- {
- auto sm = stack[start].mod;
- if (sm == m)
- break;
- assert(sm >= 0);
- if (bt(ctorstart, sm))
- ctorInCycle = true;
- }
- assert(stack[start].mod == m);
- if (ctorInCycle)
- {
- return false;
- }
- else
- {
- /* This is also a cycle, but the import chain does not constrain
- * the order of initialization, either because the imported
- * modules have no ctors or the ctors are standalone.
- */
- ++idx;
- }
- }
- else
- {
- auto curmod = _modules[m];
- if (curmod.flags & mask)
- {
- if (curmod.flags & MIstandalone || !edges[m].length)
- { // trivial ctor => sort in
- ctors[cidx++] = curmod;
- bts(ctordone, m);
- }
- else
- { // non-trivial ctor => defer
- bts(ctorstart, m);
- }
- }
- else // no ctor => mark as visited
- {
- bts(ctordone, m);
- }
-
- if (edges[m].length)
- {
- /* Internal runtime error, recursion exceeds number of modules.
- */
- (stackidx < len) || assert(0);
-
- // recurse
- stack[stackidx++] = StackRec(mods, idx);
- idx = 0;
- mods = edges[m];
- }
- }
- }
-
- if (stackidx)
- { // pop old value from stack
- --stackidx;
- mods = stack[stackidx]._mods;
- idx = stack[stackidx]._idx;
- auto m = mods[idx++];
- if (bt(ctorstart, m) && !bts(ctordone, m))
- ctors[cidx++] = _modules[m];
- }
- else // done
- break;
- }
- // store final number and shrink array
- ctors = (cast(immutable(ModuleInfo)**).realloc(ctors.ptr, cidx * size_t.sizeof))[0 .. cidx];
- return true;
- }
-
- /* Do two passes: ctor/dtor, tlsctor/tlsdtor
- */
- immutable(ModuleInfo)*[] _ctors2;
- immutable(ModuleInfo)*[] _tlsctors2;
- auto result = sort(_ctors2, MIctor | MIdtor) && sort(_tlsctors2, MItlsctor | MItlsdtor);
- if (result) // no cycle
- {
- // fall back to original ordering as part of the deprecation.
- if (_ctors.ptr)
- .free(_ctors.ptr);
- _ctors = _ctors2;
- if (_tlsctors.ptr)
- .free(_tlsctors.ptr);
- _tlsctors = _tlsctors2;
- }
- else
- {
- // free any allocated memory that will be forgotten
- if (_ctors2.ptr)
- .free(_ctors2.ptr);
- if (_tlsctors2.ptr)
- .free(_tlsctors2.ptr);
- }
- return result;
- }
-
void runCtors()
{
// run independent ctors
diff --git a/libphobos/libdruntime/rt/monitor_.d b/libphobos/libdruntime/rt/monitor_.d
index 0f1d0e9..c1f3f3c 100644
--- a/libphobos/libdruntime/rt/monitor_.d
+++ b/libphobos/libdruntime/rt/monitor_.d
@@ -54,7 +54,7 @@ extern (C) void _d_monitordelete(Object h, bool det)
}
// does not call dispose events, for internal use only
-extern (C) void _d_monitordelete_nogc(Object h) @nogc
+extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow
{
auto m = getMonitor(h);
if (m is null)
@@ -148,7 +148,7 @@ extern (C) void rt_detachDisposeEvent(Object h, DEvent e)
nothrow:
-extern (C) void _d_monitor_staticctor()
+extern (C) void _d_monitor_staticctor() @nogc nothrow
{
version (Posix)
{
@@ -158,7 +158,7 @@ extern (C) void _d_monitor_staticctor()
initMutex(&gmtx);
}
-extern (C) void _d_monitor_staticdtor()
+extern (C) void _d_monitor_staticdtor() @nogc nothrow
{
destroyMutex(&gmtx);
version (Posix)
diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d
index 26c24c4..7b55693 100644
--- a/libphobos/libdruntime/rt/util/typeinfo.d
+++ b/libphobos/libdruntime/rt/util/typeinfo.d
@@ -1,8 +1,8 @@
/**
- * This module contains utilities for TypeInfo implementation.
+ * A few predefined implementations for primitive types and arrays thereof. Also a couple of helpers.
*
* Copyright: Copyright Kenji Hara 2014-.
- * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+ * License: <a href="https://boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
* Authors: Kenji Hara
* Source: $(DRUNTIMESRC rt/util/_typeinfo.d)
*/
@@ -10,100 +10,74 @@ module rt.util.typeinfo;
import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex;
static import core.internal.hash;
-template Floating(T)
-if (is(T == float) || is(T == double) || is(T == real))
+// Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise.
+pragma(inline, true)
+private int cmp3(T)(const T lhs, const T rhs)
+if (__traits(isIntegral, T))
{
- pure nothrow @safe:
-
- bool equals(T f1, T f2)
- {
- return f1 == f2;
- }
-
- int compare(T d1, T d2)
- {
- if (d1 != d1 || d2 != d2) // if either are NaN
- {
- if (d1 != d1)
- {
- if (d2 != d2)
- return 0;
- return -1;
- }
- return 1;
- }
- return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
- }
+ static if (T.sizeof < int.sizeof)
+ // Taking the difference will always fit in an int.
+ return int(lhs) - int(rhs);
+ else
+ return (lhs > rhs) - (lhs < rhs);
+}
- public alias hashOf = core.internal.hash.hashOf;
+// Three-way compare for real fp types. NaN is smaller than all valid numbers.
+// Code is small and fast, see https://godbolt.org/z/fzb877
+pragma(inline, true)
+private int cmp3(T)(const T d1, const T d2)
+if (is(T == float) || is(T == double) || is(T == real))
+{
+ if (d2 != d2)
+ return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN.
+ // If d1 is NaN, both comparisons are false so we get -1, as needed.
+ return (d1 > d2) - !(d1 >= d2);
}
-// @@@DEPRECATED_2.105@@@
-template Floating(T)
+// Three-way compare for complex types.
+pragma(inline, true)
+private int cmp3(T)(const T f1, const T f2)
if (isComplex!T)
{
- pure nothrow @safe:
-
- bool equals(T f1, T f2)
- {
- return f1.re == f2.re && f1.im == f2.im;
- }
-
- int compare(T f1, T f2)
- {
- int result;
-
- if (f1.re < f2.re)
- result = -1;
- else if (f1.re > f2.re)
- result = 1;
- else if (f1.im < f2.im)
- result = -1;
- else if (f1.im > f2.im)
- result = 1;
- else
- result = 0;
+ if (int result = cmp3(f1.re, f2.re))
return result;
- }
-
- size_t hashOf(scope const T val)
- {
- return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im));
- }
+ return cmp3(f1.im, f2.im);
}
-template Array(T)
-if (is(T == float) || is(T == double) || is(T == real))
+unittest
{
- pure nothrow @safe:
-
- bool equals(T[] s1, T[] s2)
- {
- size_t len = s1.length;
- if (len != s2.length)
- return false;
- for (size_t u = 0; u < len; u++)
- {
- if (!Floating!T.equals(s1[u], s2[u]))
- return false;
- }
- return true;
- }
-
- int compare(T[] s1, T[] s2)
- {
- size_t len = s1.length;
- if (s2.length < len)
- len = s2.length;
- for (size_t u = 0; u < len; u++)
- {
- if (int c = Floating!T.compare(s1[u], s2[u]))
- return c;
- }
- return (s1.length > s2.length) - (s1.length < s2.length);
- }
-
- public alias hashOf = core.internal.hash.hashOf;
+ assert(cmp3(short.max, short.min) > 0);
+ assert(cmp3(42, 42) == 0);
+ assert(cmp3(int.max, int.min) > 0);
+
+ double x, y;
+ assert(cmp3(x, y) == 0);
+ assert(cmp3(y, x) == 0);
+ x = 42;
+ assert(cmp3(x, y) > 0);
+ assert(cmp3(y, x) < 0);
+ y = 43;
+ assert(cmp3(x, y) < 0);
+ assert(cmp3(y, x) > 0);
+ y = 42;
+ assert(cmp3(x, y) == 0);
+ assert(cmp3(y, x) == 0);
+
+ d_cdouble u, v;
+ assert(cmp3(u, v) == 0);
+ assert(cmp3(v, u) == 0);
+ u = d_cdouble(42, 42);
+ assert(cmp3(u, v) > 0);
+ assert(cmp3(v, u) < 0);
+ v = d_cdouble(43, 42);
+ assert(cmp3(u, v) < 0);
+ assert(cmp3(v, u) > 0);
+ v = d_cdouble(42, 43);
+ assert(cmp3(u, v) < 0);
+ assert(cmp3(v, u) > 0);
+ v = d_cdouble(42, 42);
+ assert(cmp3(u, v) == 0);
+ assert(cmp3(v, u) == 0);
}
// @@@DEPRECATED_2.105@@@
@@ -209,7 +183,7 @@ unittest
}();
}
-// Reduces to `T` if `cond` is `true` or `U` otherwise.
+// Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful.
private template Select(bool cond, T, U)
{
static if (cond) alias Select = T;
@@ -238,57 +212,38 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
static if (is(T == Base))
override size_t getHash(scope const void* p)
{
- static if (__traits(isFloating, T) || isComplex!T)
- return Floating!T.hashOf(*cast(T*)p);
- else
- return hashOf(*cast(const T *)p);
+ return hashOf(*cast(const T *)p);
}
// `equals` is the same for `Base` and `T`, introduce it just once.
static if (is(T == Base))
override bool equals(in void* p1, in void* p2)
{
- static if (__traits(isFloating, T) || isComplex!T)
- return Floating!T.equals(*cast(T*)p1, *cast(T*)p2);
- else
- return *cast(T *)p1 == *cast(T *)p2;
+ return *cast(const T *)p1 == *cast(const T *)p2;
}
// `T` and `Base` may have different signedness, so this function is introduced conditionally.
static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
override int compare(in void* p1, in void* p2)
{
- static if (__traits(isFloating, T) || isComplex!T)
- {
- return Floating!T.compare(*cast(T*)p1, *cast(T*)p2);
- }
- else static if (T.sizeof < int.sizeof)
- {
- // Taking the difference will always fit in an int.
- return int(*cast(T *) p1) - int(*cast(T *) p2);
- }
- else
- {
- auto lhs = *cast(T *) p1, rhs = *cast(T *) p2;
- return (lhs > rhs) - (lhs < rhs);
- }
+ return cmp3(*cast(const T*) p1, *cast(const T*) p2);
}
static if (is(T == Base))
- override @property size_t tsize() nothrow pure
+ override @property size_t tsize()
{
return T.sizeof;
}
static if (is(T == Base))
- override @property size_t talign() nothrow pure
+ override @property size_t talign()
{
return T.alignof;
}
// Override initializer only if necessary.
static if (is(T == Base) || T.init != Base.init)
- override const(void)[] initializer() @trusted
+ override const(void)[] initializer()
{
static if (__traits(isZeroInit, T))
{
@@ -311,7 +266,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
}
static if (is(T == Base) || RTInfo!T != RTInfo!Base)
- override @property immutable(void)* rtInfo() nothrow pure const @safe
+ override @property immutable(void)* rtInfo()
{
return RTInfo!T;
}
@@ -377,52 +332,33 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf
static if (is(T == Base))
override size_t getHash(scope const void* p) @trusted const
{
- static if (__traits(isFloating, T) || isComplex!T)
- return Array!T.hashOf(*cast(T[]*)p);
- else
- return hashOf(*cast(const T[]*) p);
+ return hashOf(*cast(const T[]*) p);
}
static if (is(T == Base))
override bool equals(in void* p1, in void* p2) const
{
- static if (__traits(isFloating, T) || isComplex!T)
- {
- return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2);
- }
- else
- {
- import core.stdc.string;
- auto s1 = *cast(T[]*)p1;
- auto s2 = *cast(T[]*)p2;
- return s1.length == s2.length &&
- memcmp(s1.ptr, s2.ptr, s1.length) == 0;
- }
+ // Just reuse the builtin.
+ return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2;
}
static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
override int compare(in void* p1, in void* p2) const
{
- static if (__traits(isFloating, T) || isComplex!T)
- {
- return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2);
- }
- else
+ // Can't reuse __cmp in object.d because that handles NaN differently.
+ // (Q: would it make sense to unify behaviors?)
+ // return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2);
+ auto lhs = *cast(const T[]*) p1;
+ auto rhs = *cast(const T[]*) p2;
+ size_t len = lhs.length;
+ if (rhs.length < len)
+ len = rhs.length;
+ for (size_t u = 0; u < len; u++)
{
- auto s1 = *cast(T[]*)p1;
- auto s2 = *cast(T[]*)p2;
- auto len = s1.length;
-
- if (s2.length < len)
- len = s2.length;
- for (size_t u = 0; u < len; u++)
- {
- if (int result = (s1[u] > s2[u]) - (s1[u] < s2[u]))
- return result;
- }
- return (s1.length > s2.length) - (s1.length < s2.length);
+ if (int result = cmp3(lhs.ptr[u], rhs.ptr[u]))
+ return result;
}
- }
+ return cmp3(lhs.length, rhs.length); }
override @property inout(TypeInfo) next() inout
{
@@ -692,52 +628,37 @@ unittest
// typeof(null)
class TypeInfo_n : TypeInfo
{
- override string toString() const @safe { return "typeof(null)"; }
+ const: pure: @nogc: nothrow: @safe:
- override size_t getHash(scope const void* p) const
- {
- return 0;
- }
+ override string toString() { return "typeof(null)"; }
- override bool equals(in void* p1, in void* p2) const @trusted
- {
- return true;
- }
+ override size_t getHash(scope const void*) { return 0; }
- override int compare(in void* p1, in void* p2) const @trusted
- {
- return 0;
- }
+ override bool equals(in void*, in void*) { return true; }
- override @property size_t tsize() const
- {
- return typeof(null).sizeof;
- }
+ override int compare(in void*, in void*) { return 0; }
- override const(void)[] initializer() const @trusted
- {
- __gshared immutable void[typeof(null).sizeof] init;
- return init;
- }
+ override @property size_t tsize() { return typeof(null).sizeof; }
- override void swap(void *p1, void *p2) const @trusted
- {
- }
+ override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
- override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; }
+ override void swap(void*, void*) {}
- unittest
+ override @property immutable(void)* rtInfo() { return rtinfoNoPointers; }
+}
+
+unittest
+{
+ with (typeid(typeof(null)))
{
- with (typeid(typeof(null)))
- {
- assert(toString == "typeof(null)");
- assert(getHash(null) == 0);
- assert(equals(null, null));
- assert(compare(null, null) == 0);
- assert(tsize == typeof(null).sizeof);
- assert(initializer == new ubyte[(void*).sizeof]);
- assert(rtInfo == rtinfoNoPointers);
- }
+ assert(toString == "typeof(null)");
+ assert(getHash(null) == 0);
+ assert(equals(null, null));
+ assert(compare(null, null) == 0);
+ assert(tsize == typeof(null).sizeof);
+ assert(initializer.ptr is null);
+ assert(initializer.length == typeof(null).sizeof);
+ assert(rtInfo == rtinfoNoPointers);
}
}