From 59ad77ad9fd898c3e1757548d6ff9e738b966609 Mon Sep 17 00:00:00 2001 From: monarchdodra Date: Mon, 17 Mar 2014 20:31:27 +0100 Subject: [PATCH] Improve emplaceRef for qualified construction --- std/algorithm.d | 6 +- std/array.d | 37 +++--- std/conv.d | 307 ++++++++++++++++++++++++++---------------------- 3 files changed, 183 insertions(+), 167 deletions(-) diff --git a/std/algorithm.d b/std/algorithm.d index a2d8ecc8364..e3c2b7badb9 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -790,7 +790,7 @@ template reduce(fun...) if (fun.length >= 1) result = void; foreach (i, T; result.Types) { - emplaceRef(result[i], seed); + emplaceRef!T(result[i], seed); } r.popFront(); return reduce(result, r); @@ -856,7 +856,7 @@ template reduce(fun...) if (fun.length >= 1) foreach (i, T; result.Types) { - emplaceRef(result[i], elem); + emplaceRef!T(result[i], elem); } } } @@ -1407,7 +1407,7 @@ void uninitializedFill(Range, Value)(Range range, Value filler) // Must construct stuff by the book for (; !range.empty; range.popFront()) - emplaceRef(range.front, filler); + emplaceRef!T(range.front, filler); } else // Doesn't matter whether fill is initialized or not diff --git a/std/array.d b/std/array.d index d32a180c443..9dac2a1d829 100644 --- a/std/array.d +++ b/std/array.d @@ -51,7 +51,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) size_t i; foreach (e; r) { - emplaceRef(result[i], e); + emplaceRef!E(result[i], e); ++i; } return cast(E[])result; @@ -110,6 +110,12 @@ unittest static assert(bug12315[0].i == 123456789); } +unittest +{ + static struct S{int* p;} + auto a = array(immutable(S).init.repeat(5)); +} + /** Convert a narrow string to an array type that fully supports random access. This is handled as a special case and always returns a $(D dchar[]), @@ -1059,13 +1065,13 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) { static if (is(E : T)) //ditto { - emplaceRef(tmp[j++], stuff[i]); + emplaceRef!T(tmp[j++], stuff[i]); } else { foreach (v; stuff[i]) { - emplaceRef(tmp[j++], v); + emplaceRef!T(tmp[j++], v); } } } @@ -2434,12 +2440,7 @@ struct Appender(A : T[], T) auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. len + 1];} auto bigData = bigDataFun(); - static if (is(Unqual!T == T)) - alias uitem = item; - else - auto ref uitem() @trusted nothrow @property { return cast(Unqual!T)item;} - - emplaceRef(bigData[len], uitem); + emplaceRef!T(bigData[len], item); //We do this at the end, in case of exceptions _data.arr = bigData; @@ -2484,28 +2485,18 @@ struct Appender(A : T[], T) auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];} auto bigData = bigDataFun(); - enum mustEmplace = is(typeof(bigData[0].opAssign(cast(Unqual!T)items.front))) || - !is(typeof(bigData[0] = cast(Unqual!T)items.front)); + alias UT = Unqual!T; - static if (is(typeof(_data.arr[] = items[])) && !mustEmplace) + static if (is(typeof(_data.arr[] = items[])) && + !hasElaborateAssign!(Unqual!T) && isAssignable!(UT, ElementEncodingType!Range)) { - //pragma(msg, T.stringof); pragma(msg, Range.stringof); bigData[len .. newlen] = items[]; } - else static if (is(Unqual!T == ElementType!Range)) - { - foreach (ref it ; bigData[len .. newlen]) - { - emplaceRef(it, items.front); - items.popFront(); - } - } else { - static auto ref getUItem(U)(U item) @trusted {return cast(Unqual!T)item;} foreach (ref it ; bigData[len .. newlen]) { - emplaceRef(it, getUItem(items.front)); + emplaceRef!T(it, items.front); items.popFront(); } } diff --git a/std/conv.d b/std/conv.d index 83243923629..7a784328fa6 100644 --- a/std/conv.d +++ b/std/conv.d @@ -3845,170 +3845,169 @@ emplace, but takes its argument by ref (as opposed to "by pointer"). This makes it easier to use, easier to be safe, and faster in a non-inline build. -+/ -package ref T emplaceRef(T)(ref T chunk) -{ - static assert (is(T* : void*), - format("Cannot emplace a %s because it is qualified.", T.stringof)); - - static assert (is(typeof({static T i;})), - format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); - return emplaceInitializer(chunk); -} -// ditto -package ref T emplaceRef(T, Args...)(ref T chunk, auto ref Args args) -if (!is(T == struct) && Args.length == 1) +Furthermore, emplaceRef takes a type paremeter, which specifies the type we +want to build. This helps to build qualified objects on mutable buffer, +without breaking the type system with unsafe casts. ++/ +package template emplaceRef(T) { - alias Arg = Args[0]; - alias arg = args[0]; - - static assert (is(T* : void*), - format("Cannot emplace a %s because it is qualified.", T.stringof)); - - static assert(is(typeof({T t = args[0];})), - format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); + alias UT = Unqual!T; - static if (isStaticArray!T) + ref UT emplaceRef()(ref UT chunk) { - alias UArg = Unqual!Arg; - alias E = typeof(chunk.ptr[0]); - enum N = T.length; + static assert (is(typeof({static T i;})), + format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); + + return emplaceInitializer(chunk); + } - static if (is(Arg : T)) + static if (!is(T == struct)) + ref UT emplaceRef(Arg)(ref UT chunk, auto ref Arg arg) + { + static assert(is(typeof({T t = arg;})), + format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); + + static if (isStaticArray!T) { - //Matching static array - static if (!hasElaborateAssign!T && isAssignable!(T, Arg)) - chunk = arg; - else static if (is(UArg == T)) + alias UArg = Unqual!Arg; + alias E = ElementEncodingType!(typeof(T.init[])); + alias UE = Unqual!E; + enum N = T.length; + + static if (is(Arg : T)) { - memcpy(&chunk, &arg, T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); + //Matching static array + static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg)) + chunk = arg; + else static if (is(UArg == UT)) + { + memcpy(&chunk, &arg, T.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(cast(void*)&chunk); + } + else + .emplaceRef!T(chunk, cast(T)arg); } - else - emplaceRef(chunk, cast(T)arg); - } - else static if (is(Arg : E[])) - { - //Matching dynamic array - static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg[]))) - chunk[] = arg[]; - else static if (is(UArg == E[])) + else static if (is(Arg : E[])) { - assert(N == chunk.length, "Array length missmatch in emplace"); - memcpy(cast(void*)&chunk, arg.ptr, T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); + //Matching dynamic array + static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg[]))) + chunk[] = arg[]; + else static if (is(Unqual!(ElementEncodingType!Arg) == UE)) + { + assert(N == chunk.length, "Array length missmatch in emplace"); + memcpy(cast(void*)&chunk, arg.ptr, T.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(cast(void*)&chunk); + } + else + .emplaceRef!T(chunk, cast(E[])arg); } - else - emplaceRef(chunk, cast(E[])arg); - } - else static if (is(Arg : E)) - { - //Case matching single element to array. - static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg))) - chunk[] = arg; - else static if (is(UArg == E)) + else static if (is(Arg : E)) { - //Note: We copy everything, and then postblit just once. - //This is as exception safe as what druntime can provide us. - foreach(i; 0 .. N) - memcpy(cast(void*)&(chunk[i]), &arg, E.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); + //Case matching single element to array. + static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) + chunk[] = arg; + else static if (is(UArg == Unqual!E)) + { + //Note: We copy everything, and then postblit just once. + //This is as exception safe as what druntime can provide us. + foreach(i; 0 .. N) + memcpy(cast(void*)&(chunk[i]), &arg, E.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(cast(void*)&chunk); + } + else + //Alias this. Coerce. + .emplaceRef!T(chunk, cast(E)arg); + } + else static if (is(typeof(.emplaceRef!E(chunk[0], arg)))) + { + //Final case for everything else: + //Types that don't match (int to uint[2]) + //Recursion for multidimensions + static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) + chunk[] = arg; + else + foreach(i; 0 .. N) + .emplaceRef!E(chunk[i], arg); } else - //Alias this. Coerce. - emplaceRef(chunk, cast(E)arg); + static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); + + return chunk; } - else static if (is(typeof(emplaceRef(chunk[0], arg)))) + else { - //Final case for everything else: - //Types that don't match (int to uint[2]) - //Recursion for multidimensions - static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg))) - chunk[] = arg; - else - foreach(i; 0 .. N) - emplaceRef(chunk[i], arg); + chunk = arg; + return chunk; } - else - static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); - - return chunk; } - else + // ditto + static if (is(T == struct)) + ref UT emplaceRef(Args...)(ref UT chunk, auto ref Args args) { - chunk = arg; - return chunk; - } -} -// ditto -package ref T emplaceRef(T, Args...)(ref T chunk, auto ref Args args) -if (is(T == struct)) -{ - static assert (is(T* : void*), - format("Cannot emplace a %s because it is qualified.", T.stringof)); - - static if (Args.length == 1 && is(Args[0] : T) && - is (typeof({T t = args[0];})) //Check for legal postblit - ) - { - static if (is(T == Unqual!(Args[0]))) + static if (Args.length == 1 && is(Args[0] : T) && + is (typeof({T t = args[0];})) //Check for legal postblit + ) { - //Types match exactly: we postblit - static if (!hasElaborateAssign!T && isAssignable!T) - chunk = args[0]; + static if (is(Unqual!T == Unqual!(Args[0]))) + { + //Types match exactly: we postblit + static if (!hasElaborateAssign!UT && isAssignable!(UT, T)) + chunk = args[0]; + else + { + memcpy(&chunk, &args[0], T.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(&chunk); + } + } else + //Alias this. Coerce to type T. + .emplaceRef!T(chunk, cast(T)args[0]); + } + else static if (is(typeof(chunk.__ctor(args)))) + { + // T defines a genuine constructor accepting args + // Go the classic route: write .init first, then call ctor + emplaceInitializer(chunk); + chunk.__ctor(args); + } + else static if (is(typeof(T.opCall(args)))) + { + //Can be built calling opCall + emplaceOpCaller(chunk, args); //emplaceOpCaller is deprecated + } + else static if (is(typeof(T(args)))) + { + // Struct without constructor that has one matching field for + // each argument. Individually emplace each field + emplaceInitializer(chunk); + foreach (i, ref field; chunk.tupleof[0 .. Args.length]) { - memcpy(&chunk, &args[0], T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(&chunk); + alias Field = typeof(field); + alias UField = Unqual!Field; + static if (is(Field == UField)) + .emplaceRef!Field(field, args[i]); + else + .emplaceRef!Field(*cast(Unqual!Field*)&field, args[i]); } } else - //Alias this. Coerce to type T. - emplaceRef(chunk, cast(T)args[0]); - } - else static if (is(typeof(chunk.__ctor(args)))) - { - // T defines a genuine constructor accepting args - // Go the classic route: write .init first, then call ctor - emplaceInitializer(chunk); - chunk.__ctor(args); - } - else static if (is(typeof(T.opCall(args)))) - { - //Can be built calling opCall - emplaceOpCaller(chunk, args); //emplaceOpCaller is deprecated - } - else static if (is(typeof(T(args)))) - { - // Struct without constructor that has one matching field for - // each argument. Individually emplace each field - emplaceInitializer(chunk); - foreach (i, ref field; chunk.tupleof[0 .. Args.length]) { - alias Field = typeof(field); - static if (is(Field == Unqual!Field)) - emplaceRef(field, args[i]); - else - emplaceRef(*cast(Unqual!Field*)&field, args[i]); + //We can't emplace. Try to diagnose a disabled postblit. + static assert(!(Args.length == 1 && is(Args[0] : T)), + format("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); + + //We can't emplace. + static assert(false, + format("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); } + + return chunk; } - else - { - //We can't emplace. Try to diagnose a disabled postblit. - static assert(!(Args.length == 1 && is(Args[0] : T)), - format("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); - - //We can't emplace. - static assert(false, - format("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); - } - - return chunk; } //emplace helper functions private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow @@ -4027,7 +4026,7 @@ ref T emplaceOpCaller(T, Args...)(ref T chunk, auto ref Args args) { static assert (is(typeof({T t = T.opCall(args);})), format("%s.opCall does not return adequate data for construction.", T.stringof)); - return emplaceRef(chunk, chunk.opCall(args)); + return emplaceRef!T(chunk, chunk.opCall(args)); } @@ -4042,7 +4041,7 @@ as $(D chunk)). */ T* emplace(T)(T* chunk) @safe nothrow pure { - emplaceRef(*chunk); + emplaceRef!T(*chunk); return chunk; } @@ -4060,14 +4059,14 @@ as $(D chunk)). T* emplace(T, Args...)(T* chunk, auto ref Args args) if (!is(T == struct) && Args.length == 1) { - emplaceRef(*chunk, args); + emplaceRef!T(*chunk, args); return chunk; } /// ditto T* emplace(T, Args...)(T* chunk, auto ref Args args) if (is(T == struct)) { - emplaceRef(*chunk, args); + emplaceRef!T(*chunk, args); return chunk; } @@ -4858,6 +4857,32 @@ unittest emplace(&sss, s); } +unittest //Constness +{ + import std.stdio; + + int a = void; + emplaceRef!(const int)(a, 5); + + immutable i = 5; + const(int)* p = void; + emplaceRef!(const int*)(p, &i); + + struct S + { + int* p; + } + alias IS = immutable(S); + S s = void; + emplaceRef!IS(s, IS()); + S[2] ss = void; + emplaceRef!(IS[2])(ss, IS()); + + IS[2] iss = IS.init; + emplaceRef!(IS[2])(ss, iss); + emplaceRef!(IS[2])(ss, iss[]); +} + private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) { enforceEx!ConvException(chunk.length >= typeSize,