From eebb1b55350eba5b0d1400e8727c3f968b66fb18 Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Tue, 19 Mar 2013 12:41:47 +0100 Subject: [PATCH 1/4] Make array CTFE-able also improve arrayAllocImpl, and bugfix + unittest --- std/array.d | 78 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/std/array.d b/std/array.d index 4bb853d2368..c377b6129a4 100644 --- a/std/array.d +++ b/std/array.d @@ -169,6 +169,18 @@ unittest } } +unittest +{ + //CTFE + struct S + { + ubyte[5] a = repeat(cast(ubyte)1)[0 .. 5].array(); + ubyte[5] b = iota(cast(ubyte)0, cast(ubyte)5).array(); + } + static assert(S.init.a[] == [1, 1, 1, 1, 1]); + static assert(S.init.b[] == [0, 1, 2, 3, 4]); +} + /** Returns a newly allocated associative array out of elements of the input range, which must be a range of tuples (Key, Value). @@ -266,10 +278,10 @@ assert(matrix[0].length == 31); --- ), $(ARGS), $(ARGS), $(ARGS import std.array;)) */ -auto uninitializedArray(T, I...)(I sizes) -if(allSatisfy!(isIntegral, I)) +auto uninitializedArray(T, Sizes...)(Sizes sizes) + if (allSatisfy!(isIntegral, Sizes)) { - return arrayAllocImpl!(false, T, I)(sizes); + return arrayAllocImpl!(false, T, Sizes)(sizes); } unittest @@ -287,10 +299,10 @@ Returns a new array of type $(D T) allocated on the garbage collected heap. Initialization is guaranteed only for pointers, references and slices, for preservation of memory safety. */ -auto minimallyInitializedArray(T, I...)(I sizes) @trusted -if(allSatisfy!(isIntegral, I)) +auto minimallyInitializedArray(T, Sizes...)(Sizes sizes) @trusted + if (allSatisfy!(isIntegral, Sizes)) { - return arrayAllocImpl!(true, T, I)(sizes); + return arrayAllocImpl!(true, T, Sizes)(sizes); } unittest @@ -306,34 +318,56 @@ unittest } } -private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) -if(allSatisfy!(isIntegral, I)) +unittest +{ + alias T1 = int*[]; + alias T2 = int*[][]; + auto t1 = minimallyInitializedArray!T1(10); + auto t2 = minimallyInitializedArray!T2(10, 10); + foreach (p; t1) + assert(p is null); + foreach (arr; t2) + foreach (p; arr) + assert(p is null); +} + +private auto arrayAllocImpl(bool minimallyInitialized, T, Sizes...)(Sizes sizes) + if (allSatisfy!(isIntegral, Sizes)) { - static assert(sizes.length >= 1, + static assert(Sizes.length >= 1, "Cannot allocate an array without the size of at least the first " ~ " dimension."); - static assert(sizes.length <= nDimensions!T, - to!string(sizes.length) ~ " dimensions specified for a " ~ + static assert(Sizes.length <= nDimensions!T, + to!string(Sizes.length) ~ " dimensions specified for a " ~ to!string(nDimensions!T) ~ " dimensional array."); alias typeof(T.init[0]) E; + immutable size0 = sizes[0]; - auto ptr = cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!(E)); - auto ret = ptr[0..sizes[0]]; - - static if(sizes.length > 1) + static if (Sizes.length == 1) { + if (__ctfe) + return new E[](size0); + else + static if (minimallyInitialized && hasIndirections!E) + return new E[](size0); + else + return (cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E))[0 .. size0]; + } + else + { + E[] ret; + if (__ctfe) + ret = new E[](size0); + else + ret = (cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E))[0 .. size0]; + foreach(ref elem; ret) { - elem = uninitializedArray!(E)(sizes[1..$]); + elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1 .. $]); } + return ret; } - else static if(minimallyInitialized && hasIndirections!E) - { - ret[] = E.init; - } - - return ret; } /** From 43387ab0a177049759c7cf58a967f754b7b2f4aa Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Sun, 24 Mar 2013 13:25:01 +0100 Subject: [PATCH 2/4] Issue 9803 - minimallyInitializedArray fails past "1D" depth --- std/array.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/array.d b/std/array.d index c377b6129a4..8f0fc66b4b8 100644 --- a/std/array.d +++ b/std/array.d @@ -318,7 +318,7 @@ unittest } } -unittest +unittest //@@@9803@@@ { alias T1 = int*[]; alias T2 = int*[][]; From 2bdc30bb7aaf22f7c89ca8f1ba85ff7bcbe8f9b7 Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Mon, 25 Mar 2013 14:36:44 +0100 Subject: [PATCH 3/4] Further improve arrayAllocImpl "greatly" Simplifies implementation, while increasing efficiency. Also makes function @safe when initialization occurs. --- std/array.d | 68 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/std/array.d b/std/array.d index 8f0fc66b4b8..7d45b10111c 100644 --- a/std/array.d +++ b/std/array.d @@ -320,15 +320,20 @@ unittest unittest //@@@9803@@@ { - alias T1 = int*[]; - alias T2 = int*[][]; - auto t1 = minimallyInitializedArray!T1(10); - auto t2 = minimallyInitializedArray!T2(10, 10); - foreach (p; t1) - assert(p is null); - foreach (arr; t2) - foreach (p; arr) + //Check safety while we're at it. + void foo() @safe nothrow + { + alias T1 = int*[]; + alias T2 = int*[][]; + auto t1 = minimallyInitializedArray!T1(10); + auto t2 = minimallyInitializedArray!T2(10, 10); + foreach (p; t1) assert(p is null); + foreach (arr; t2) + foreach (p; arr) + assert(p is null); + } + foo(); } private auto arrayAllocImpl(bool minimallyInitialized, T, Sizes...)(Sizes sizes) @@ -341,35 +346,44 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, Sizes...)(Sizes sizes) to!string(Sizes.length) ~ " dimensions specified for a " ~ to!string(nDimensions!T) ~ " dimensional array."); - alias typeof(T.init[0]) E; - immutable size0 = sizes[0]; - - static if (Sizes.length == 1) + static if (minimallyInitialized && + hasIndirections!(ArrayAllocImplFinalElementType!(T, Sizes.length))) { - if (__ctfe) - return new E[](size0); - else - static if (minimallyInitialized && hasIndirections!E) - return new E[](size0); - else - return (cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E))[0 .. size0]; + //If we end up needing to initialize the elements anyways, then we + //might as well just let the compiler allocate for us. + return new T(sizes); } else { - E[] ret; + //If ctfe, just call new. if (__ctfe) - ret = new E[](size0); - else - ret = (cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E))[0 .. size0]; + return new T(sizes); - foreach(ref elem; ret) - { - elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1 .. $]); - } + alias E = ElementEncodingType!T; + + //Manually build the array without initializing: + //Builds the top dimension + E[] ret = (cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E))[0 .. sizes[0]]; + //Recursivelly builds the next dimensions + static if (Sizes.length > 1) + foreach(ref elem; ret) + elem = arrayAllocImpl!(false, E)(sizes[1 .. $]); return ret; } } +//Private template: Gets the final element type of a multi-dimensional-array +//of dimension N. Used by minimallyInitializedArray to check for indirections. +private template ArrayAllocImplFinalElementType(T, size_t N) +{ + static if (N > 1) + alias ArrayAllocImplFinalElementType = ArrayAllocImplFinalElementType!(ElementEncodingType!T, N - 1); + else static if (N == 1) + alias ArrayAllocImplFinalElementType = ElementEncodingType!T; + else + static assert(false); +} + /** Implements the range interface primitive $(D empty) for built-in arrays. Due to the fact that nonmember functions can be called with From 8954fc753beb261d1ed5cec1a9a9d3a6941499f6 Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Mon, 25 Mar 2013 17:02:33 +0100 Subject: [PATCH 4/4] more tweaks --- std/array.d | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/std/array.d b/std/array.d index 7d45b10111c..5e956e61a75 100644 --- a/std/array.d +++ b/std/array.d @@ -253,16 +253,37 @@ private template nDimensions(T) } } -unittest { +unittest +{ static assert(nDimensions!(uint[]) == 1); static assert(nDimensions!(float[][]) == 2); } +// Returns the element type of the nDimension array T. +private template NElementEncodingType(T, size_t N) +{ + static if (N > 1) + alias NElementEncodingType = NElementEncodingType!(ElementEncodingType!T, N - 1); + else static if (N == 1) + alias NElementEncodingType = ElementEncodingType!T; + else + static assert(false); +} + +unittest +{ + static assert(is(NElementEncodingType!(uint[], 1) == uint)); + static assert(is(NElementEncodingType!(uint[][], 1) == uint[])); + static assert(is(NElementEncodingType!(uint[][], 2) == uint )); + static assert(is(NElementEncodingType!(char[][], 1) == char[])); + static assert(is(NElementEncodingType!(char[][], 2) == char )); +} + /** Returns a new array of type $(D T) allocated on the garbage collected heap without initializing its elements. This can be a useful optimization if every element will be immediately initialized. $(D T) may be a multidimensional -array. In this case sizes may be specified for any number of dimensions from 1 +array. In this case sizes may be specified for any number of dimensions from 1 to the number in $(D T). Examples: @@ -296,7 +317,7 @@ unittest /** Returns a new array of type $(D T) allocated on the garbage collected heap. -Initialization is guaranteed only for pointers, references and slices, +Initialization is guaranteed only for types which have indirections, for preservation of memory safety. */ auto minimallyInitializedArray(T, Sizes...)(Sizes sizes) @trusted @@ -347,7 +368,7 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, Sizes...)(Sizes sizes) to!string(nDimensions!T) ~ " dimensional array."); static if (minimallyInitialized && - hasIndirections!(ArrayAllocImplFinalElementType!(T, Sizes.length))) + hasIndirections!(NElementEncodingType!(T, Sizes.length))) { //If we end up needing to initialize the elements anyways, then we //might as well just let the compiler allocate for us. @@ -372,18 +393,6 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, Sizes...)(Sizes sizes) } } -//Private template: Gets the final element type of a multi-dimensional-array -//of dimension N. Used by minimallyInitializedArray to check for indirections. -private template ArrayAllocImplFinalElementType(T, size_t N) -{ - static if (N > 1) - alias ArrayAllocImplFinalElementType = ArrayAllocImplFinalElementType!(ElementEncodingType!T, N - 1); - else static if (N == 1) - alias ArrayAllocImplFinalElementType = ElementEncodingType!T; - else - static assert(false); -} - /** Implements the range interface primitive $(D empty) for built-in arrays. Due to the fact that nonmember functions can be called with