diff --git a/mak/COPY b/mak/COPY index 84ec5dec32..8aa6b8109f 100644 --- a/mak/COPY +++ b/mak/COPY @@ -33,6 +33,7 @@ COPY=\ $(IMPDIR)\core\internal\utf.d \ $(IMPDIR)\core\internal\lifetime.d \ \ + $(IMPDIR)\core\internal\array\appending.d \ $(IMPDIR)\core\internal\array\comparison.d \ $(IMPDIR)\core\internal\array\construction.d \ $(IMPDIR)\core\internal\array\equality.d \ diff --git a/mak/DOCS b/mak/DOCS index fadec9beb6..0b003227d0 100644 --- a/mak/DOCS +++ b/mak/DOCS @@ -73,6 +73,7 @@ DOCS=\ $(DOCDIR)\core_sys_darwin_mach_thread_act.html \ $(DOCDIR)\core_sys_darwin_netinet_in_.html \ \ + $(DOCDIR)\core_internal_array_appending.html \ $(DOCDIR)\core_internal_array_capacity.html \ $(DOCDIR)\core_internal_array_casting.html \ $(DOCDIR)\core_internal_array_comparison.html \ diff --git a/mak/SRCS b/mak/SRCS index c6025b07d7..fb3c5ac3b3 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -33,6 +33,7 @@ SRCS=\ src\core\internal\utf.d \ src\core\internal\lifetime.d \ \ + src\core\internal\array\appending.d \ src\core\internal\array\comparison.d \ src\core\internal\array\construction.d \ src\core\internal\array\equality.d \ diff --git a/mak/WINDOWS b/mak/WINDOWS index 8bbcb0f341..ba5087806b 100644 --- a/mak/WINDOWS +++ b/mak/WINDOWS @@ -153,6 +153,9 @@ $(IMPDIR)\core\internal\utf.d : src\core\internal\utf.d $(IMPDIR)\core\internal\lifetime.d : src\core\internal\lifetime.d copy $** $@ +$(IMPDIR)\core\internal\array\appending.d : src\core\internal\array\appending.d + copy $** $@ + $(IMPDIR)\core\internal\array\comparison.d : src\core\internal\array\comparison.d copy $** $@ diff --git a/src/core/internal/array/appending.d b/src/core/internal/array/appending.d new file mode 100644 index 0000000000..1a6dcad4c4 --- /dev/null +++ b/src/core/internal/array/appending.d @@ -0,0 +1,237 @@ +/** + This module contains support for controlling dynamic arrays' appending + + Copyright: Copyright Digital Mars 2000 - 2019. + License: Distributed under the + $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + (See accompanying file LICENSE) + Source: $(DRUNTIMESRC core/_internal/_array/_appending.d) +*/ +module core.internal.array.appending; + +/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX) +private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) @trusted pure nothrow; + +/// Implementation of `_d_arrayappendcTX` and `_d_arrayappendcTXTrace` +template _d_arrayappendcTXImpl(Tarr : T[], T) +{ + import core.internal.array.utils : _d_HookTraceImpl, isPostblitNoThrow; + + private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!"; + + /** + * Extend an array `px` by `n` elements. + * Caller must initialize those elements. + * Params: + * px = the array that will be extended, taken as a reference + * n = how many new elements to extend it with + * Returns: + * The new value of `px` + * Bugs: + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. + */ + static if (isPostblitNoThrow!T) // `nothrow` deduction doesn't work, so this is needed + ref Tarr _d_arrayappendcTX(return scope ref Tarr px, size_t n) @trusted pure nothrow + { + pragma(inline, false); + + mixin(_d_arrayappendcTXBody); + } + else + ref Tarr _d_arrayappendcTX(return scope ref Tarr px, size_t n) @trusted pure nothrow + { + pragma(inline, false); + + mixin(_d_arrayappendcTXBody); + } + + private enum _d_arrayappendcTXBody = q{ + version (D_TypeInfo) + { + auto ti = typeid(Tarr); + + // _d_arrayappendcTX takes the `px` as a ref byte[], but its length + // should still be the original length + auto pxx = (cast(byte*)px.ptr)[0 .. px.length]; + ._d_arrayappendcTX(ti, pxx, n); + px = (cast(T*)pxx.ptr)[0 .. pxx.length]; + + return px; + } + else + assert(0, "Cannot append arrays if compiling without support for runtime type information!"); + }; + + /** + * TraceGC wrapper around $(REF _d_arrayappendcTX, rt,array,appending,_d_arrayappendcTXImpl). + * Bugs: + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. + */ + alias _d_arrayappendcTXTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendcTX, errorMessage); +} + +/// Implementation of `_d_arrayappendT` and `_d_arrayappendTTrace` +template _d_arrayappendTImpl(Tarr : T[], T) +{ + import core.internal.array.utils : _d_HookTraceImpl, isPostblitNoThrow; + + private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!"; + + /** + * Append array `y` to array `x`. + * Params: + * x = what array to append to, taken as a reference + * y = what should be appended + * Returns: + * The new value of `x` + * Bugs: + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. + */ + static if (isPostblitNoThrow!T) + ref Tarr _d_arrayappendT(return scope ref Tarr x, scope Tarr y) @trusted pure nothrow + { + pragma(inline, false); + + mixin(_d_arrayappendTBody); + } + else + ref Tarr _d_arrayappendT(return scope ref Tarr x, scope Tarr y) @trusted pure + { + pragma(inline, false); + + mixin(_d_arrayappendTBody); + } + + private enum _d_arrayappendTBody = q{ + import core.stdc.string : memcpy; + import core.internal.traits : Unqual; + + auto length = x.length; + auto sizeelem = T.sizeof; + + _d_arrayappendcTXImpl!Tarr._d_arrayappendcTX(x, y.length); + + if (y.length) + memcpy(cast(Unqual!T *)&x[length], cast(Unqual!T *)&y[0], y.length * sizeelem); + + // do postblit + __doPostblit(cast(Unqual!Tarr)x[length .. length + y.length]); + return x; + }; + + /** + * TraceGC wrapper around $(REF _d_arrayappendT, rt,array,appending,_d_arrayappendTImpl). + * Bugs: + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. + */ + alias _d_arrayappendTTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendT, errorMessage); +} + +/** + * Run postblit on `t` if it is a struct and needs it. + * Or if `t` is a array, run it on the children if they have a postblit. + */ +private void __doPostblit(T)(auto ref T t) @trusted pure +{ + import core.internal.traits : hasElaborateCopyConstructor; + + static if (is(T == struct)) + { + // run the postblit function incase the struct has one + static if (__traits(hasMember, T, "__xpostblit") && + // Bugzilla 14746: Check that it's the exact member of S. + __traits(isSame, T, __traits(parent, t.__xpostblit))) + t.__xpostblit(); + } + else static if (is(T U : U[]) && hasElaborateCopyConstructor!U) + { + // only do a postblit if the `U` requires it. + foreach (ref el; t) + __doPostblit(el); + } +} + +@safe unittest +{ + double[] arr1; + foreach (i; 0 .. 4) + _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, [cast(double)i]); + assert(arr1 == [0.0, 1.0, 2.0, 3.0]); +} + +@safe unittest +{ + int blitted; + struct Item + { + this(this) + { + blitted++; + } + } + + Item[] arr1 = [Item(), Item()]; + Item[] arr2 = [Item(), Item()]; + Item[] arr1_org = [Item(), Item()]; + arr1_org ~= arr2; + _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2); + + // postblit should have triggered on atleast the items in arr2 + assert(blitted >= arr2.length); +} + +@safe unittest +{ + int blitted; + struct Item + { + this(this) + { + blitted++; + } + } + + Item[][] arr1 = [[Item()]]; + Item[][] arr2 = [[Item()]]; + + _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2); + + // no postblit should have happend because arr{1,2} contains dynamic arrays + assert(blitted == 0); +} + +@safe unittest +{ + int blitted; + struct Item + { + this(this) + { + blitted++; + } + } + + Item[1][] arr1 = [[Item()]]; + Item[1][] arr2 = [[Item()]]; + + _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2); + // postblit should have happend because arr{1,2} contains static arrays + assert(blitted >= arr2.length); +} + +@safe unittest +{ + string str; + _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "a"); + _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "b"); + _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "c"); + assert(str == "abc"); +} diff --git a/src/core/internal/array/utils.d b/src/core/internal/array/utils.d index 75f50152e8..2b637c092b 100644 --- a/src/core/internal/array/utils.d +++ b/src/core/internal/array/utils.d @@ -45,11 +45,11 @@ private ulong accumulatePure(string file, int line, string funcname, string name * funcname = Function that called `_d_HookTraceImpl` * parameters = Parameters that will be used to call `Hook` * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, + * This function template needs be between the compiler and a much older runtime hook that bypassed safety, * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. + * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. */ -auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(string file, int line, string funcname, Parameters!Hook parameters) @trusted pure nothrow +auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(string file, int line, string funcname, Parameters!Hook parameters) @trusted pure { version (D_TypeInfo) { @@ -92,3 +92,30 @@ auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(string file, int line, assert(0, errorMessage); } +/** + * Check if the function `F` is calleable in a `nothrow` scope. + * Params: + * F = Function that does not take any parameters + * Returns: + * if the function is callable in a `nothrow` scope. + */ +enum isNoThrow(alias F) = is(typeof(() nothrow { F(); })); + +/** + * Check if the type `T`'s postblit is called in nothrow, if it exist + * Params: + * T = Type to check + * Returns: + * if the postblit is callable in a `nothrow` scope, if it exist. + * if it does not exist, return true. + */ +template isPostblitNoThrow(T) { + static if (__traits(isStaticArray, T)) + enum isPostblitNoThrow = isPostblitNoThrow!(typeof(T.init[0])); + else static if (__traits(hasMember, T, "__xpostblit") && + // Bugzilla 14746: Check that it's the exact member of S. + __traits(isSame, T, __traits(parent, T.init.__xpostblit))) + enum isPostblitNoThrow = isNoThrow!(T.init.__xpostblit); + else + enum isPostblitNoThrow = true; +}; diff --git a/src/object.d b/src/object.d index 7b220de58d..d284e0f78c 100644 --- a/src/object.d +++ b/src/object.d @@ -38,6 +38,10 @@ alias dstring = immutable(dchar)[]; version (D_ObjectiveC) public import core.attribute : selector; +/// See $(REF _d_arrayappendTImpl, core,internal,array,appending) +public import core.internal.array.appending : _d_arrayappendTImpl; +/// See $(REF _d_arrayappendcTXImpl, core,internal,array,appending) +public import core.internal.array.appending : _d_arrayappendcTXImpl; /// See $(REF __cmp, core,internal,array,comparison) public import core.internal.array.comparison : __cmp; /// See $(REF __equals, core,internal,array,equality) diff --git a/src/rt/lifetime.d b/src/rt/lifetime.d index 383afb5e84..5182b6a20b 100644 --- a/src/rt/lifetime.d +++ b/src/rt/lifetime.d @@ -2064,24 +2064,24 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) { // c could encode into from 1 to 4 characters char[4] buf = void; - byte[] appendthis; // passed to appendT + char[] appendthis; // passed to appendT if (c <= 0x7F) { buf.ptr[0] = cast(char)c; - appendthis = (cast(byte *)buf.ptr)[0..1]; + appendthis = buf[0..1]; } else if (c <= 0x7FF) { buf.ptr[0] = cast(char)(0xC0 | (c >> 6)); buf.ptr[1] = cast(char)(0x80 | (c & 0x3F)); - appendthis = (cast(byte *)buf.ptr)[0..2]; + appendthis = buf[0..2]; } else if (c <= 0xFFFF) { buf.ptr[0] = cast(char)(0xE0 | (c >> 12)); buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf.ptr[2] = cast(char)(0x80 | (c & 0x3F)); - appendthis = (cast(byte *)buf.ptr)[0..3]; + appendthis = buf[0..3]; } else if (c <= 0x10FFFF) { @@ -2089,7 +2089,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf.ptr[3] = cast(char)(0x80 | (c & 0x3F)); - appendthis = (cast(byte *)buf.ptr)[0..4]; + appendthis = buf[0..4]; } else { @@ -2102,7 +2102,12 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) // get a typeinfo from the compiler. Assuming shared is the safest option. // Once the compiler is fixed, the proper typeinfo should be forwarded. // - return _d_arrayappendT(typeid(shared char[]), x, appendthis); + + // 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); + x = cast(byte[])xx; + return x; } unittest @@ -2141,21 +2146,17 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) { // c could encode into from 1 to 2 w characters wchar[2] buf = void; - byte[] appendthis; // passed to appendT + wchar[] appendthis; // passed to appendT if (c <= 0xFFFF) { buf.ptr[0] = cast(wchar) c; - // note that although we are passing only 1 byte here, appendT - // interprets this as being an array of wchar, making the necessary - // casts. - appendthis = (cast(byte *)buf.ptr)[0..1]; + appendthis = buf[0..1]; } else { buf.ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); buf.ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00); - // ditto from above. - appendthis = (cast(byte *)buf.ptr)[0..2]; + appendthis = buf[0..2]; } // @@ -2163,7 +2164,11 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) // get a typeinfo from the compiler. Assuming shared is the safest option. // Once the compiler is fixed, the proper typeinfo should be forwarded. // - return _d_arrayappendT(typeid(shared wchar[]), x, appendthis); + + auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length]; + object._d_arrayappendTImpl!(shared(wchar)[])._d_arrayappendT(xx, cast(shared(wchar)[])appendthis); + x = (cast(byte*)xx.ptr)[0 .. xx.length]; + return x; }