diff --git a/mak/COPY b/mak/COPY index 43f6b76f90..b641c2f392 100644 --- a/mak/COPY +++ b/mak/COPY @@ -13,8 +13,9 @@ COPY=\ $(IMPDIR)\core\time.d \ $(IMPDIR)\core\vararg.d \ \ - $(IMPDIR)\core\internal\hash.d \ $(IMPDIR)\core\internal\convert.d \ + $(IMPDIR)\core\internal\hash.d \ + $(IMPDIR)\core\internal\traits.d \ \ $(IMPDIR)\core\stdc\complex.d \ $(IMPDIR)\core\stdc\config.d \ diff --git a/mak/MANIFEST b/mak/MANIFEST index a8911e69ba..a6834abbf0 100644 --- a/mak/MANIFEST +++ b/mak/MANIFEST @@ -33,8 +33,9 @@ MANIFEST=\ src\core\time.d \ src\core\vararg.d \ \ - src\core\internal\hash.d \ src\core\internal\convert.d \ + src\core\internal\hash.d \ + src\core\internal\traits.d \ \ src\core\stdc\complex.d \ src\core\stdc\config.d \ diff --git a/mak/SRCS b/mak/SRCS index 77f011d10f..69c1707add 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -14,8 +14,9 @@ SRCS=\ src\core\time.d \ src\core\vararg.d \ \ - src\core\internal\hash.d \ src\core\internal\convert.d \ + src\core\internal\hash.d \ + src\core\internal\traits.d \ \ src\core\stdc\config.d \ src\core\stdc\ctype.d \ diff --git a/src/core/internal/convert.d b/src/core/internal/convert.d index 5ebb9811e8..9aebfa0292 100644 --- a/src/core/internal/convert.d +++ b/src/core/internal/convert.d @@ -8,6 +8,7 @@ * Source: $(DRUNTIMESRC core/internal/_convert.d) */ module core.internal.convert; +import core.internal.traits : Unqual; @trusted pure nothrow const(ubyte)[] toUbyte(T)(ref T val) if(is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) || @@ -478,15 +479,6 @@ template floatFormat(T) if(is(T:real) || is(T:ireal)) static assert(0); } -private template Unqual(T) -{ - static if (is(T U == shared(const U))) alias U Unqual; - else static if (is(T U == const U )) alias U Unqual; - else static if (is(T U == immutable U )) alias U Unqual; - else static if (is(T U == inout U )) alias U Unqual; - else static if (is(T U == shared U )) alias U Unqual; - else alias T Unqual; -} // all toUbyte functions must be evaluable at compile time @trusted pure nothrow diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d new file mode 100644 index 0000000000..9d691a8881 --- /dev/null +++ b/src/core/internal/traits.d @@ -0,0 +1,54 @@ +/** + * Contains traits for runtime internal usage. + * + * Copyright: Copyright Digital Mars 2014 -. + * License: Boost License 1.0. + * Authors: Martin Nowak + * Source: $(DRUNTIMESRC core/internal/_traits.d) + */ +module core.internal.traits; + +/// taken from std.typetuple.TypeTuple +template TypeTuple(TList...) +{ + alias TypeTuple = TList; +} + +T trustedCast(T, U)(auto ref U u) @trusted pure nothrow +{ + return cast(T)u; +} + +template Unconst(T) +{ + static if (is(T U == immutable U)) alias Unconst = U; + else static if (is(T U == inout const U)) alias Unconst = U; + else static if (is(T U == inout U)) alias Unconst = U; + else static if (is(T U == const U)) alias Unconst = U; + else alias Unconst = T; +} + +/// taken from std.traits.Unqual +template Unqual(T) +{ + version (none) // Error: recursive alias declaration @@@BUG1308@@@ + { + static if (is(T U == const U)) alias Unqual = Unqual!U; + else static if (is(T U == immutable U)) alias Unqual = Unqual!U; + else static if (is(T U == inout U)) alias Unqual = Unqual!U; + else static if (is(T U == shared U)) alias Unqual = Unqual!U; + else alias Unqual = T; + } + else // workaround + { + static if (is(T U == immutable U)) alias Unqual = U; + else static if (is(T U == shared inout const U)) alias Unqual = U; + else static if (is(T U == shared inout U)) alias Unqual = U; + else static if (is(T U == shared const U)) alias Unqual = U; + else static if (is(T U == shared U)) alias Unqual = U; + else static if (is(T U == inout const U)) alias Unqual = U; + else static if (is(T U == inout U)) alias Unqual = U; + else static if (is(T U == const U)) alias Unqual = U; + else alias Unqual = T; + } +} diff --git a/src/core/time.d b/src/core/time.d index b3368a680f..c3db70179e 100644 --- a/src/core/time.d +++ b/src/core/time.d @@ -78,6 +78,7 @@ module core.time; import core.exception; import core.stdc.time; import core.stdc.stdio; +import core.internal.traits : _Unqual = Unqual; version(Windows) { @@ -3316,38 +3317,6 @@ string numToString(long value) @safe pure nothrow } -/+ A copy of std.traits.Unqual. +/ -private template _Unqual(T) -{ - version (none) // Error: recursive alias declaration @@@BUG1308@@@ - { - static if (is(T U == const U)) alias _Unqual!U _Unqual; - else static if (is(T U == immutable U)) alias _Unqual!U _Unqual; - else static if (is(T U == shared U)) alias _Unqual!U _Unqual; - else alias T _Unqual; - } - else // workaround - { - static if (is(T U == shared(const U))) alias U _Unqual; - else static if (is(T U == const U )) alias U _Unqual; - else static if (is(T U == immutable U )) alias U _Unqual; - else static if (is(T U == shared U )) alias U _Unqual; - else alias T _Unqual; - } -} - -unittest -{ - static assert(is(_Unqual!(int) == int)); - static assert(is(_Unqual!(const int) == int)); - static assert(is(_Unqual!(immutable int) == int)); - static assert(is(_Unqual!(shared int) == int)); - static assert(is(_Unqual!(shared(const int)) == int)); - alias immutable(int[]) ImmIntArr; - static assert(is(_Unqual!(ImmIntArr) == immutable(int)[])); -} - - /+ A copy of std.typecons.TypeTuple. +/ private template _TypeTuple(TList...) { diff --git a/src/object.di b/src/object.di index 35995b8df1..e742845c2a 100644 --- a/src/object.di +++ b/src/object.di @@ -628,3 +628,95 @@ version (unittest) } } } + +private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; + +/// Provide the .dup array 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."); + + // 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); +} + +/// ditto +// const overload to support implicit conversion to immutable (unique result, see DIP29) +T[] dup(T)(const(T)[] a) + if (is(const(T) : 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); +} + +/// Provide the .idup array property. +immutable(T)[] idup(T)(T[] a) +{ + 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); +} + +private U[] _trustedDup(T, U)(T[] a) @trusted +{ + return _dup!(T, U)(a); +} + +private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit +{ + if (__ctfe) + { + U[] res; + foreach (ref e; a) + res ~= e; + return res; + } + + import core.stdc.string : memcpy; + + auto arr = _d_newarrayU(typeid(T[]), a.length); + memcpy(cast(void*)arr.ptr, cast(void*)a.ptr, T.sizeof * a.length); + auto res = *cast(typeof(return)*)&arr; + _doPostblit(res); + return res; +} + +private void _doPostblit(T)(T[] ary) +{ + // infer static postblit type, run postblit if any + static if (is(T == struct)) + { + import core.internal.traits : Unqual; + + alias PostBlitT = typeof(function(void*){T a = T.init, b = a;}); + // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/... + auto postBlit = cast(PostBlitT)typeid(Unqual!T).xpostblit; + if (postBlit !is null) + { + foreach (ref el; ary) + postBlit(cast(void*)&el); + } + } + else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit) + { + alias PostBlitT = typeof(delegate(void*){T a = T.init, b = a;}); + auto postBlit = cast(PostBlitT)&typeid(T).postblit; + + foreach (ref el; ary) + postBlit(cast(void*)&el); + } +} diff --git a/src/object_.d b/src/object_.d index 7231367416..5dd1ab8e0e 100644 --- a/src/object_.d +++ b/src/object_.d @@ -2751,3 +2751,192 @@ unittest int[S[]] aa = [[S(11)] : 13]; assert(aa[[S(12)]] == 13); // fails } + +private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; + +/// Provide the .dup array 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."); + + // 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); +} + +/// ditto +// const overload to support implicit conversion to immutable (unique result, see DIP29) +T[] dup(T)(const(T)[] a) + if (is(const(T) : 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); +} + +/// Provide the .idup array property. +immutable(T)[] idup(T)(T[] a) +{ + 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); +} + +private U[] _trustedDup(T, U)(T[] a) @trusted +{ + return _dup!(T, U)(a); +} + +private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit +{ + if (__ctfe) + { + U[] res; + foreach (ref e; a) + res ~= e; + return res; + } + + import core.stdc.string : memcpy; + + auto arr = _d_newarrayU(typeid(T[]), a.length); + memcpy(cast(void*)arr.ptr, cast(void*)a.ptr, T.sizeof * a.length); + auto res = *cast(typeof(return)*)&arr; + _doPostblit(res); + return res; +} + +private void _doPostblit(T)(T[] ary) +{ + // infer static postblit type, run postblit if any + static if (is(T == struct)) + { + import core.internal.traits : Unqual; + + alias PostBlitT = typeof(function(void*){T a = T.init, b = a;}); + // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/... + auto postBlit = cast(PostBlitT)typeid(Unqual!T).xpostblit; + if (postBlit !is null) + { + foreach (ref el; ary) + postBlit(cast(void*)&el); + } + } + else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit) + { + alias PostBlitT = typeof(delegate(void*){T a = T.init, b = a;}); + auto postBlit = cast(PostBlitT)&typeid(T).postblit; + + foreach (ref el; ary) + postBlit(cast(void*)&el); + } +} + +unittest +{ + static struct S1 { int* p; } + static struct S2 { @disable this(); } + static struct S3 { @disable this(this); } + + int dg1() pure nothrow @safe + { + { + char[] m; + string i; + m = dup(m); + i = idup(i); + m = dup(i); + i = idup(m); + } + { + S1[] m; + immutable(S1)[] i; + m = dup(m); + i = idup(i); + static assert(!is(typeof(idup(m)))); + static assert(!is(typeof(dup(i)))); + } + { + S3[] m; + immutable(S3)[] i; + static assert(!is(typeof(dup(m)))); + static assert(!is(typeof(idup(i)))); + } + { + shared(S1)[] m; + m = dup(m); + static assert(!is(typeof(idup(m)))); + } + { + int[] a = (inout(int)) { inout(const(int))[] a; return dup(a); }(0); + } + return 1; + } + + int dg2() pure nothrow @safe + { + { + S2[] m = [S2.init, S2.init]; + immutable(S2)[] i = [S2.init, S2.init]; + m = dup(m); + m = dup(i); + i = idup(m); + i = idup(i); + } + return 2; + } + + enum a = dg1(); + enum b = dg2(); + assert(dg1() == a); + assert(dg2() == b); +} + +unittest +{ + static struct Sunpure { this(this) @safe nothrow {} } + static struct Sthrow { this(this) @safe pure {} } + static struct Sunsafe { this(this) @system pure nothrow {} } + + 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([]); })); + + 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([]); })); +} + +unittest +{ + 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(); } +} + +unittest +{ + auto a = [1, 2, 3]; + auto b = a.dup(); + assert(b.capacity >= 3); +} diff --git a/src/rt/lifetime.d b/src/rt/lifetime.d index a459ac32b6..df5e8b8973 100644 --- a/src/rt/lifetime.d +++ b/src/rt/lifetime.d @@ -198,7 +198,7 @@ private class ArrayAllocLengthLock where elem0 starts 16 bytes after the first byte. */ -bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength = ~0) +bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength = ~0) pure nothrow { if(info.size <= 256) { @@ -210,13 +210,15 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, si { if(isshared) { - synchronized(typeid(ArrayAllocLengthLock)) + try synchronized(typeid(ArrayAllocLengthLock)) { if(*length == cast(ubyte)oldlength) *length = cast(ubyte)newlength; else return false; } + catch (Throwable t) + assert(0, "Failed to synchronize."); } else { @@ -242,13 +244,15 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, si { if(isshared) { - synchronized(typeid(ArrayAllocLengthLock)) + try synchronized(typeid(ArrayAllocLengthLock)) { if(*length == oldlength) *length = cast(ushort)newlength; else return false; } + catch (Throwable t) + assert(0, "Failed to synchronize."); } else { @@ -274,13 +278,15 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, si { if(isshared) { - synchronized(typeid(ArrayAllocLengthLock)) + try synchronized(typeid(ArrayAllocLengthLock)) { if(*length == oldlength) *length = newlength; else return false; } + catch (Throwable t) + assert(0, "Failed to synchronize."); } else { @@ -747,64 +753,60 @@ Lcontinue: } /** - * Allocate a new array of length elements. + * Allocate a new uninitialized array of length elements. * ti is the type of the resulting array, or pointer to element. - * (For when the array is initialized to 0) */ -extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) +extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow { - void[] result; - auto size = ti.next.tsize; // array element size + auto size = ti.next.tsize; - debug(PRINTF) printf("_d_newarrayT(length = x%x, size = %d)\n", length, size); + debug(PRINTF) printf("_d_newarrayU(length = x%x, size = %d)\n", length, size); if (length == 0 || size == 0) - result = null; - else + return null; + + version (D_InlineAsm_X86) { - version (D_InlineAsm_X86) + asm { - asm - { - mov EAX,size ; - mul EAX,length ; - mov size,EAX ; - jc Loverflow ; - } + mov EAX,size ; + mul EAX,length ; + mov size,EAX ; + jc Loverflow ; } - else version(D_InlineAsm_X86_64) + } + else version(D_InlineAsm_X86_64) + { + asm { - asm - { - mov RAX,size ; - mul RAX,length ; - mov size,RAX ; - jc Loverflow ; - } + mov RAX,size ; + mul RAX,length ; + mov size,RAX ; + jc Loverflow ; } - else - { - auto newsize = size * length; - if (newsize / length != size) - goto Loverflow; + } + else + { + auto newsize = size * length; + if (newsize / length != size) + goto Loverflow; - size = newsize; - } + size = newsize; + } - // increase the size by the array pad. - auto pad = __arrayPad(size); - if (size + pad < size) - goto Loverflow; + // increase the size by the array pad. + auto pad = __arrayPad(size); + if (size + pad < size) + goto Loverflow; - auto info = GC.qalloc(size + pad, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); - debug(PRINTF) printf(" p = %p\n", info.base); - // update the length of the array - auto arrstart = __arrayStart(info); - memset(arrstart, 0, size); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, size, isshared); - result = arrstart[0..length]; + { + auto info = GC.qalloc(size + pad, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + debug(PRINTF) printf(" p = %p\n", info.base); + // update the length of the array + auto arrstart = __arrayStart(info); + auto isshared = typeid(ti) is typeid(TypeInfo_Shared); + __setArrayAllocLength(info, size, isshared); + return arrstart[0..length]; } - return result; Loverflow: onOutOfMemoryError(); @@ -812,81 +814,48 @@ Loverflow: } /** - * For when the array has a non-zero initializer. + * 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) */ -extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) +extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow { - void[] result; - auto size = ti.next.tsize; // array element size + void[] result = _d_newarrayU(ti, length); + auto size = ti.next.tsize; - debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size); + memset(result.ptr, 0, size * length); + return result; +} - if (length == 0 || size == 0) - result = null; - else - { - auto initializer = ti.next.init(); - auto isize = initializer.length; - auto q = initializer.ptr; - version (D_InlineAsm_X86) - { - asm - { - mov EAX,size ; - mul EAX,length ; - mov size,EAX ; - jc Loverflow ; - } - } - else version (D_InlineAsm_X86_64) - { - asm - { - mov RAX,size ; - mul RAX,length ; - mov size,RAX ; - jc Loverflow ; - } - } - else - { - auto newsize = size * length; - if (newsize / length != size) - goto Loverflow; +/** + * For when the array has a non-zero initializer. + */ +extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow +{ + import core.internal.traits : TypeTuple; - size = newsize; - } + void[] result = _d_newarrayU(ti, length); + auto size = ti.next.tsize; - auto info = GC.qalloc(size + __arrayPad(size), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); - debug(PRINTF) printf(" p = %p\n", info.base); - auto arrstart = __arrayStart(info); - if (isize == 1) - memset(arrstart, *cast(ubyte*)q, size); - else if (isize == int.sizeof) - { - int init = *cast(int*)q; - auto len = size / int.sizeof; - for (size_t u = 0; u < len; u++) - { - (cast(int*)arrstart)[u] = init; - } - } - else - { - for (size_t u = 0; u < size; u += isize) - { - memcpy(arrstart + u, q, isize); - } - } - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, size, isshared); - result = arrstart[0..length]; + auto init = ti.next.init(); + + switch (init.length) + { + foreach (T; TypeTuple!(ubyte, ushort, uint, ulong)) + { + case T.sizeof: + (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr; + return result; } - return result; -Loverflow: - onOutOfMemoryError(); - assert(0); + default: + { + immutable sz = init.length; + for (size_t u = 0; u < size * length; u += sz) + memcpy(result.ptr + u, init.ptr, sz); + return result; + } + } } @@ -2236,9 +2205,10 @@ struct Array2 /** - * + * Replaced by object.dup and object.idup. + * Remove in 2.068. */ -extern (C) void[] _adDupT(const TypeInfo ti, void[] a) +deprecated extern (C) void[] _adDupT(const TypeInfo ti, void[] a) out (result) { auto sizeelem = ti.next.tsize; // array element size diff --git a/win32.mak b/win32.mak index 1516a2aec4..b8a22bd37e 100644 --- a/win32.mak +++ b/win32.mak @@ -182,10 +182,13 @@ $(IMPDIR)\core\time.d : src\core\time.d $(IMPDIR)\core\vararg.d : src\core\vararg.d copy $** $@ +$(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d + copy $** $@ + $(IMPDIR)\core\internal\hash.d : src\core\internal\hash.d copy $** $@ -$(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d +$(IMPDIR)\core\internal\traits.d : src\core\internal\traits.d copy $** $@ $(IMPDIR)\core\stdc\complex.d : src\core\stdc\complex.d diff --git a/win64.mak b/win64.mak index 87d8bc3e6e..68746aa6a7 100644 --- a/win64.mak +++ b/win64.mak @@ -189,10 +189,13 @@ $(IMPDIR)\core\time.d : src\core\time.d $(IMPDIR)\core\vararg.d : src\core\vararg.d copy $** $@ +$(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d + copy $** $@ + $(IMPDIR)\core\internal\hash.d : src\core\internal\hash.d copy $** $@ -$(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d +$(IMPDIR)\core\internal\traits.d : src\core\internal\traits.d copy $** $@ $(IMPDIR)\core\stdc\complex.d : src\core\stdc\complex.d