Merge pull request #2015 from monarchdodra/emplaceQual

Improve emplaceRef for qualified construction
This commit is contained in:
Andrei Alexandrescu 2014-03-26 18:03:39 -07:00
commit cecd745cef
3 changed files with 191 additions and 175 deletions

View file

@ -799,7 +799,7 @@ template reduce(fun...) if (fun.length >= 1)
result = void; result = void;
foreach (i, T; result.Types) foreach (i, T; result.Types)
{ {
emplaceRef(result[i], seed); emplaceRef!T(result[i], seed);
} }
r.popFront(); r.popFront();
return reduce(result, r); return reduce(result, r);
@ -865,7 +865,7 @@ template reduce(fun...) if (fun.length >= 1)
foreach (i, T; result.Types) foreach (i, T; result.Types)
{ {
emplaceRef(result[i], elem); emplaceRef!T(result[i], elem);
} }
} }
} }
@ -1456,7 +1456,7 @@ void uninitializedFill(Range, Value)(Range range, Value filler)
// Must construct stuff by the book // Must construct stuff by the book
for (; !range.empty; range.popFront()) for (; !range.empty; range.popFront())
emplaceRef(range.front, filler); emplaceRef!T(range.front, filler);
} }
else else
// Doesn't matter whether fill is initialized or not // Doesn't matter whether fill is initialized or not

View file

@ -51,7 +51,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
size_t i; size_t i;
foreach (e; r) foreach (e; r)
{ {
emplaceRef(result[i], e); emplaceRef!E(result[i], e);
++i; ++i;
} }
return cast(E[])result; return cast(E[])result;
@ -110,6 +110,12 @@ unittest
static assert(bug12315[0].i == 123456789); 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. 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[]), 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 static if (is(E : T)) //ditto
{ {
emplaceRef(tmp[j++], stuff[i]); emplaceRef!T(tmp[j++], stuff[i]);
} }
else else
{ {
foreach (v; stuff[i]) 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 bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. len + 1];}
auto bigData = bigDataFun(); auto bigData = bigDataFun();
static if (is(Unqual!T == T)) emplaceRef!T(bigData[len], item);
alias uitem = item;
else
auto ref uitem() @trusted nothrow @property { return cast(Unqual!T)item;}
emplaceRef(bigData[len], uitem);
//We do this at the end, in case of exceptions //We do this at the end, in case of exceptions
_data.arr = bigData; _data.arr = bigData;
@ -2484,28 +2485,18 @@ struct Appender(A : T[], T)
auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];} auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];}
auto bigData = bigDataFun(); auto bigData = bigDataFun();
enum mustEmplace = is(typeof(bigData[0].opAssign(cast(Unqual!T)items.front))) || alias UT = Unqual!T;
!is(typeof(bigData[0] = cast(Unqual!T)items.front));
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[]; 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 else
{ {
static auto ref getUItem(U)(U item) @trusted {return cast(Unqual!T)item;}
foreach (ref it ; bigData[len .. newlen]) foreach (ref it ; bigData[len .. newlen])
{ {
emplaceRef(it, getUItem(items.front)); emplaceRef!T(it, items.front);
items.popFront(); items.popFront();
} }
} }

View file

@ -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 This makes it easier to use, easier to be safe, and faster in a non-inline
build. build.
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 ref T emplaceRef(T)(ref T chunk) package template emplaceRef(T)
{ {
static assert (is(T* : void*), alias UT = Unqual!T;
format("Cannot emplace a %s because it is qualified.", T.stringof));
static assert (is(typeof({static T i;})), ref UT emplaceRef()(ref UT chunk)
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)
{
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));
static if (isStaticArray!T)
{ {
alias UArg = Unqual!Arg; static assert (is(typeof({static T i;})),
alias E = typeof(chunk.ptr[0]); format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof));
enum N = T.length;
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 alias UArg = Unqual!Arg;
static if (!hasElaborateAssign!T && isAssignable!(T, Arg)) alias E = ElementEncodingType!(typeof(T.init[]));
chunk = arg; alias UE = Unqual!E;
else static if (is(UArg == T)) enum N = T.length;
static if (is(Arg : T))
{ {
memcpy(&chunk, &arg, T.sizeof); //Matching static array
static if (hasElaborateCopyConstructor!T) static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg))
typeid(T).postblit(cast(void*)&chunk); 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 static if (is(Arg : E[]))
{
//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 static if (is(Arg : E))
{
//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 else
emplaceRef(chunk, cast(T)arg); static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof));
}
else static if (is(Arg : E[])) return chunk;
{
//Matching dynamic array
static if (!hasElaborateAssign!T && is(typeof(chunk[] = arg[])))
chunk[] = arg[];
else static if (is(UArg == 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);
}
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))
{
//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(chunk, cast(E)arg);
}
else static if (is(typeof(emplaceRef(chunk[0], arg))))
{
//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);
} }
else 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
{
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])))
{ {
//Types match exactly: we postblit chunk = arg;
static if (!hasElaborateAssign!T && isAssignable!T) return chunk;
chunk = args[0]; }
else }
// ditto
static if (is(T == struct))
ref UT emplaceRef(Args...)(ref UT chunk, auto ref Args args)
{
static if (Args.length == 1 && is(Args[0] : T) &&
is (typeof({T t = args[0];})) //Check for legal postblit
)
{
static if (is(Unqual!T == Unqual!(Args[0])))
{ {
memcpy(&chunk, &args[0], T.sizeof); //Types match exactly: we postblit
static if (hasElaborateCopyConstructor!T) static if (!hasElaborateAssign!UT && isAssignable!(UT, T))
typeid(T).postblit(&chunk); 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])
{
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 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); //We can't emplace. Try to diagnose a disabled postblit.
static if (is(Field == Unqual!Field)) static assert(!(Args.length == 1 && is(Args[0] : T)),
emplaceRef(field, args[i]); format("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof));
else
emplaceRef(*cast(Unqual!Field*)&field, args[i]); //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 //emplace helper functions
private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow 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);})), static assert (is(typeof({T t = T.opCall(args);})),
format("%s.opCall does not return adequate data for construction.", T.stringof)); 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 T* emplace(T)(T* chunk) @safe nothrow pure
{ {
emplaceRef(*chunk); emplaceRef!T(*chunk);
return chunk; return chunk;
} }
@ -4060,14 +4059,14 @@ as $(D chunk)).
T* emplace(T, Args...)(T* chunk, auto ref Args args) T* emplace(T, Args...)(T* chunk, auto ref Args args)
if (!is(T == struct) && Args.length == 1) if (!is(T == struct) && Args.length == 1)
{ {
emplaceRef(*chunk, args); emplaceRef!T(*chunk, args);
return chunk; return chunk;
} }
/// ditto /// ditto
T* emplace(T, Args...)(T* chunk, auto ref Args args) T* emplace(T, Args...)(T* chunk, auto ref Args args)
if (is(T == struct)) if (is(T == struct))
{ {
emplaceRef(*chunk, args); emplaceRef!T(*chunk, args);
return chunk; return chunk;
} }
@ -4858,6 +4857,32 @@ unittest
emplace(&sss, s); 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) private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName)
{ {
enforceEx!ConvException(chunk.length >= typeSize, enforceEx!ConvException(chunk.length >= typeSize,