Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Fix issue 20468 - emplace should forward argument lvalueness correctly #2890

Merged
merged 1 commit into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 16 additions & 17 deletions src/core/internal/lifetime.d
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module core.internal.lifetime;

import core.lifetime : forward;

/+
emplaceRef is a package function for druntime internal use. It works like
emplace, but takes its argument by ref (as opposed to "by pointer").
Expand All @@ -23,30 +25,27 @@ void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
else static if (
!is(T == struct) && Args.length == 1 /* primitives, enums, arrays */
||
Args.length == 1 && is(typeof({T t = args[0];})) /* conversions */
Args.length == 1 && is(typeof({T t = forward!(args[0]);})) /* conversions */
||
is(typeof(T(args))) /* general constructors */)
is(typeof(T(forward!args))) /* general constructors */)
{
static struct S
{
T payload;
this(ref Args x)
this()(auto ref Args args)
{
static if (Args.length == 1)
static if (is(typeof(payload = x[0])))
payload = x[0];
else
payload = T(x[0]);
static if (is(typeof(payload = forward!args)))
payload = forward!args;
else
payload = T(x);
payload = T(forward!args);
}
}
if (__ctfe)
{
static if (is(typeof(chunk = T(args))))
chunk = T(args);
else static if (args.length == 1 && is(typeof(chunk = args[0])))
chunk = args[0];
static if (is(typeof(chunk = T(forward!args))))
chunk = T(forward!args);
else static if (args.length == 1 && is(typeof(chunk = forward!(args[0]))))
chunk = forward!(args[0]);
else assert(0, "CTFE emplace doesn't support "
~ T.stringof ~ " from " ~ Args.stringof);
}
Expand All @@ -55,14 +54,14 @@ void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
S* p = () @trusted { return cast(S*) &chunk; }();
static if (UT.sizeof > 0)
emplaceInitializer(*p);
p.__ctor(args);
p.__ctor(forward!args);
}
}
else static if (is(typeof(chunk.__ctor(args))))
else static if (is(typeof(chunk.__ctor(forward!args))))
{
// This catches the rare case of local types that keep a frame pointer
emplaceInitializer(chunk);
chunk.__ctor(args);
chunk.__ctor(forward!args);
}
else
{
Expand All @@ -82,7 +81,7 @@ static import core.internal.traits;
void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
if (is(UT == core.internal.traits.Unqual!UT))
{
emplaceRef!(UT, UT)(chunk, args);
emplaceRef!(UT, UT)(chunk, forward!args);
}

//emplace helper functions
Expand Down
35 changes: 30 additions & 5 deletions src/core/lifetime.d
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ T* emplace(T, Args...)(T* chunk, auto ref Args args)
{
import core.internal.lifetime : emplaceRef;

emplaceRef!T(*chunk, args);
emplaceRef!T(*chunk, forward!args);
return chunk;
}

Expand Down Expand Up @@ -606,7 +606,8 @@ version (unittest) private class __conv_EmplaceTestClass
S1 s1 = void;
emplace(&s1, 1);
assert(s1.i == 1);
static assert(!__traits(compiles, emplace(&s1, S1.init)));
static assert(!__traits(compiles, emplace(&s1, s1))); // copy disabled
static assert(__traits(compiles, emplace(&s1, move(s1)))); // move not affected

static struct S2
{
Expand All @@ -624,15 +625,17 @@ version (unittest) private class __conv_EmplaceTestClass
}
SS1 ss1 = void;
emplace(&ss1);
static assert(!__traits(compiles, emplace(&ss1, SS1.init)));
static assert(!__traits(compiles, emplace(&ss1, ss1))); // copying disabled
static assert(__traits(compiles, emplace(&ss1, move(ss1)))); // move unaffected

static struct SS2
{
S2 s;
}
SS2 ss2 = void;
emplace(&ss2);
static assert(!__traits(compiles, emplace(&ss2, SS2.init)));
static assert(!__traits(compiles, emplace(&ss2, ss2))); // copying disabled
static assert(__traits(compiles, emplace(&ss2, SS2.init))); // move is OK


// SS1 sss1 = s1; //This doesn't compile
Expand Down Expand Up @@ -1135,7 +1138,7 @@ pure nothrow @safe /* @nogc */ unittest
}

Unsafe unsafe = void;
static assert(!__traits(compiles, emplaceRef(unsafe, Unsafe())));
static assert(!__traits(compiles, emplaceRef(unsafe, unsafe)));

Unsafe[1] unsafeArr = [Unsafe()];
Unsafe[1] uninitializedUnsafeArr = void;
Expand Down Expand Up @@ -1197,6 +1200,28 @@ pure nothrow @safe /* @nogc */ unittest
// need ctor args
static assert(!is(typeof(emplace!A(buf))));
}

//constructor arguments forwarding
@system unittest
{
static struct S
{
this()(auto ref long arg)
{
// assert that arg is an lvalue
static assert(__traits(isRef, arg));
}
this()(auto ref double arg)
// assert that arg is an rvalue
{
static assert(!__traits(isRef, arg));
}
}
S obj = void;
long i;
emplace(&obj, i); // lvalue
emplace(&obj, 0.0); // rvalue
}
// Bulk of emplace unittests ends here

/**
Expand Down