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

Commit

Permalink
Merge pull request #2890 from SSoulaimane/forwardemplace
Browse files Browse the repository at this point in the history
Fix issue 20468 - `emplace` should forward argument lvalueness correctly
merged-on-behalf-of: Nicholas Wilson <thewilsonator@users.noreply.github.com>
  • Loading branch information
dlang-bot committed Jan 8, 2020
2 parents 998fcbc + f8b2223 commit 1c7b4cb
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 22 deletions.
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

0 comments on commit 1c7b4cb

Please sign in to comment.