diff --git a/std/conv.d b/std/conv.d index 4a194962fb2..5e21cef6876 100644 --- a/std/conv.d +++ b/std/conv.d @@ -3405,10 +3405,9 @@ as $(D chunk)). T* emplace(T)(T* chunk) if (!is(T == class)) { - auto result = cast(typeof(return)) chunk; - static T i; - memcpy(result, &i, T.sizeof); - return result; + static T i; // Can't use `= T.init` here because of @@@BUG8902@@@. + memcpy(chunk, &i, T.sizeof); + return chunk; } ///ditto T* emplace(T)(T* chunk) @@ -3418,6 +3417,64 @@ T* emplace(T)(T* chunk) return chunk; } +version(unittest) private struct __conv_EmplaceTest +{ + int i = 3; + this(int i) + { + assert(this.i == 3 && i == 5); + this.i = i; + } + this(int i, ref int j) + { + assert(i == 5 && j == 6); + this.i = i; + ++j; + } + +@disable: + this(); + this(this); + void opAssign(); +} + +version(unittest) private class __conv_EmplaceTestClass +{ + int i = 3; + this(int i) + { + assert(this.i == 3 && i == 5); + this.i = i; + } + this(int i, ref int j) + { + assert(i == 5 && j == 6); + this.i = i; + ++j; + } +} + +unittest +{ + struct S { @disable this(); } + S s = void; + static assert(!__traits(compiles, emplace(&s))); +} + +unittest +{ + interface I {} + class K : I {} + + K k = void; + emplace(&k); + assert(k is null); + + I i = void; + emplace(&i); + assert(i is null); +} + /** Given a pointer $(D chunk) to uninitialized memory (but already typed @@ -3437,39 +3494,144 @@ T* emplace(T, Args...)(T* chunk, Args args) return chunk; } +unittest +{ + debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); + int a; + int b = 42; + assert(*emplace!int(&a, b) == 42); +} + +unittest +{ + interface I {} + class K : I {} + + K k = null, k2 = new K; + assert(k !is k2); + emplace!K(&k, k2); + assert(k is k2); + + I i = null; + assert(i !is k); + emplace!I(&i, k); + assert(i is k); +} + // Specialization for struct -T* emplace(T, Args...)(T* chunk, Args args) +T* emplace(T, Args...)(T* chunk, auto ref Args args) if (is(T == struct)) { - auto result = cast(typeof(return)) chunk; - void initialize() { - static T i; - memcpy(chunk, &i, T.sizeof); + if(auto p = typeid(T).init().ptr) + memcpy(chunk, p, T.sizeof); + else + memset(chunk, 0, T.sizeof); } - static if (is(typeof(result.__ctor(args)))) + static if (is(typeof(chunk.__ctor(args)))) { // T defines a genuine constructor accepting args // Go the classic route: write .init first, then call ctor initialize(); - result.__ctor(args); + chunk.__ctor(args); } else static if (is(typeof(T(args)))) { // Struct without constructor that has one matching field for // each argument - *result = T(args); + *chunk = T(args); } else //static if (Args.length == 1 && is(Args[0] : T)) { static assert(Args.length == 1); //static assert(0, T.stringof ~ " " ~ Args.stringof); // initialize(); - *result = args[0]; + *chunk = args[0]; } - return result; + return chunk; +} + +// Test constructor branch + +unittest +{ + debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); + struct S + { + double x = 5, y = 6; + this(int a, int b) + { + assert(x == 5 && y == 6); + x = a; + y = b; + } + } + + auto s1 = new void[S.sizeof]; + auto s2 = S(42, 43); + assert(*emplace!S(cast(S*) s1.ptr, s2) == s2); + assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45)); +} + +unittest +{ + __conv_EmplaceTest k = void; + emplace(&k, 5); + assert(k.i == 5); +} + +unittest +{ + int var = 6; + __conv_EmplaceTest k = void; + emplace(&k, 5, var); + assert(k.i == 5); + assert(var == 7); +} + +// Test matching fields branch + +unittest +{ + struct S { uint n; } + S s; + emplace!S(&s, 2U); + assert(s.n == 2); +} + +unittest +{ + struct S { int a, b; this(int){} } + S s; + static assert(!__traits(compiles, emplace!S(&s, 2, 3))); +} + +unittest +{ + struct S { int a, b = 7; } + S s1 = void, s2 = void; + + emplace!S(&s1, 2); + assert(s1.a == 2 && s1.b == 7); + + emplace!S(&s2, 2, 3); + assert(s2.a == 2 && s2.b == 3); +} + +// Test assignment branch + +// FIXME: no tests + +private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) +{ + enforceEx!ConvException(chunk.length >= typeSize, + xformat("emplace: Chunk size too small: %s < %s size = %s", + chunk.length, typeName, typeSize)); + enforceEx!ConvException((cast(size_t) chunk.ptr) % typeAlignment == 0, + xformat("emplace: Misaligned memory block (0x%X): it must be %s-byte aligned for type %s", + chunk.ptr, typeAlignment, typeName)); } /** @@ -3485,14 +3647,11 @@ $(D T) is $(D @safe). Returns: A pointer to the newly constructed object. */ -T emplace(T, Args...)(void[] chunk, Args args) if (is(T == class)) +T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class)) { enum classSize = __traits(classInstanceSize, T); - enforce(chunk.length >= classSize, - new ConvException("emplace: chunk size too small")); - auto a = cast(size_t) chunk.ptr; - enforce(a % T.alignof == 0, text(a, " vs. ", T.alignof)); - auto result = cast(typeof(return)) chunk.ptr; + testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof); + auto result = cast(T) chunk.ptr; // Initialize the object in its pre-ctor state (cast(byte[]) chunk)[0 .. classSize] = typeid(T).init[]; @@ -3513,6 +3672,14 @@ T emplace(T, Args...)(void[] chunk, Args args) if (is(T == class)) return result; } +unittest +{ + int var = 6; + auto k = emplace!__conv_EmplaceTestClass(new void[__traits(classInstanceSize, __conv_EmplaceTestClass)], 5, var); + assert(k.i == 5); + assert(var == 7); +} + /** Given a raw memory area $(D chunk), constructs an object of non-$(D class) type $(D T) at that address. The constructor is passed the @@ -3525,15 +3692,11 @@ $(D T) is $(D @safe). Returns: A pointer to the newly constructed object. */ -T* emplace(T, Args...)(void[] chunk, Args args) +T* emplace(T, Args...)(void[] chunk, auto ref Args args) if (!is(T == class)) { - enforce(chunk.length >= T.sizeof, - new ConvException("emplace: chunk size too small")); - auto a = cast(size_t) chunk.ptr; - enforce(a % T.alignof == 0, text(a, " vs. ", T.alignof)); - auto result = cast(typeof(return)) chunk.ptr; - return emplace(result, args); + testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof); + return emplace(cast(T*) chunk.ptr, args); } unittest @@ -3552,26 +3715,10 @@ unittest unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - int a; - int b = 42; - assert(*emplace!int(&a, b) == 42); - - struct S - { - double x = 5, y = 6; - this(int a, int b) - { - assert(x == 5 && y == 6); - x = a; - y = b; - } - } - - auto s1 = new void[S.sizeof]; - auto s2 = S(42, 43); - assert(*emplace!S(cast(S*) s1.ptr, s2) == s2); - assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45)); + int var = 6; + auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var); + assert(k.i == 5); + assert(var == 7); } unittest @@ -3610,38 +3757,6 @@ unittest assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345])); } -unittest -{ - struct Foo - { - uint num; - } - - Foo foo; - emplace!Foo(&foo, 2U); - assert(foo.num == 2); -} - -unittest -{ - interface I {} - class K : I {} - - K k = void; - emplace!K(&k); - assert(k is null); - K k2 = new K; - assert(k2 !is null); - emplace!K(&k, k2); - assert(k is k2); - - I i = void; - emplace!I(&i); - assert(i is null); - emplace!I(&i, k); - assert(i is k); -} - // Undocumented for the time being void toTextRange(T, W)(T value, W writer) if (isIntegral!T && isOutputRange!(W, char)) diff --git a/std/traits.d b/std/traits.d index daeca45d31d..5a76fde0f31 100644 --- a/std/traits.d +++ b/std/traits.d @@ -2781,6 +2781,43 @@ unittest } +private template maxAlignment(U...) if(isTypeTuple!U) +{ + static if(U.length == 1) + enum maxAlignment = U[0].alignof; + else + enum maxAlignment = max(U[0].alignof, .maxAlignment!(U[1 .. $])); +} + + +/** +Returns class instance alignment. + +Example: +--- +class A { byte b; } +class B { long l; } + +// As class instance always has a hidden pointer +static assert(classInstanceAlignment!A == (void*).alignof); +static assert(classInstanceAlignment!B == long.alignof); +--- + */ +template classInstanceAlignment(T) if(is(T == class)) +{ + alias maxAlignment!(void*, typeof(T.tupleof)) classInstanceAlignment; +} + +unittest +{ + class A { byte b; } + class B { long l; } + + static assert(classInstanceAlignment!A == (void*).alignof); + static assert(classInstanceAlignment!B == long.alignof); +} + + //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// // Type Conversion //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// diff --git a/std/typecons.d b/std/typecons.d index 3601c5fb8bc..d275acbeef3 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -2478,7 +2478,7 @@ if (!is(T == class)) private Impl* _store; - private void initialize(A...)(A args) + private void initialize(A...)(auto ref A args) { _store = cast(Impl*) enforce(malloc(Impl.sizeof)); if (hasIndirections!T) @@ -2533,7 +2533,7 @@ Constructor that initializes the payload. Postcondition: $(D refCountedIsInitialized) */ - this(A...)(A args) if (A.length > 0) + this(A...)(auto ref A args) if (A.length > 0) { _refCounted.initialize(args); } @@ -2704,6 +2704,16 @@ unittest alias RefCounted!S SRC; } +// 6436 +unittest +{ + struct S { this(ref int val) { assert(val == 3); ++val; } } + + int val = 3; + auto s = RefCounted!S(val); + assert(val == 4); +} + unittest { RefCounted!int a; @@ -3061,27 +3071,31 @@ unittest } ---- */ -@system auto scoped(T, Args...)(Args args) if (is(T == class)) +@system auto scoped(T, Args...)(auto ref Args args) if (is(T == class)) { + // _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for + // small objects). We will just use the maximum of filed alignments. + alias classInstanceAlignment!T alignment; + alias _alignUp!alignment aligned; + static struct Scoped(T) { - private - { - // _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for - // small objects). We will just use the maximum of filed alignments. - alias maxAlignment!(void*, typeof(T.tupleof)) alignment; + // Addition of `alignment` is required as `Scoped_store` can be misaligned in memory. + private void[aligned(__traits(classInstanceSize, T) + size_t.sizeof) + alignment] Scoped_store = void; - static size_t aligned(size_t n) - { - enum badEnd = alignment - 1; // 0b11, 0b111, ... - return (n + badEnd) & ~badEnd; - } - - void[aligned(__traits(classInstanceSize, T)) + alignment] Scoped_store = void; - } @property inout(T) Scoped_payload() inout { - return cast(inout(T)) cast(void*) aligned(cast(size_t) Scoped_store.ptr); + void* alignedStore = cast(void*) aligned(cast(size_t) Scoped_store.ptr); + // As `Scoped` can be unaligned moved in memory class instance should be moved accordingly. + immutable size_t d = alignedStore - Scoped_store.ptr; + size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof]; + if(d != *currD) + { + import core.stdc.string; + memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T)); + *currD = d; + } + return cast(inout(T)) alignedStore; } alias Scoped_payload this; @@ -3099,16 +3113,17 @@ unittest } Scoped!T result; - emplace!(Unqual!T)(cast(void[])result.Scoped_store, args); + immutable size_t d = cast(void*) result.Scoped_payload - result.Scoped_store.ptr; + *cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d; + emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args); return result; } -private template maxAlignment(U...) if(isTypeTuple!U) +private size_t _alignUp(size_t alignment)(size_t n) + if(alignment > 0 && !((alignment - 1) & alignment)) { - static if(U.length == 1) - enum maxAlignment = U[0].alignof; - else - enum maxAlignment = max(U[0].alignof, .maxAlignment!(U[1 .. $])); + enum badEnd = alignment - 1; // 0b11, 0b111, ... + return (n + badEnd) & ~badEnd; } unittest // Issue 6580 testcase @@ -3127,18 +3142,26 @@ unittest // Issue 6580 testcase static assert(scoped!C7().sizeof % alignment == 0); enum longAlignment = long.alignof; - static class C1long { long l; byte b; } - static class C2long { byte[2] b; long l; } + static class C1long + { + long long_; byte byte_ = 4; + this() { } + this(long _long, ref int i) { long_ = _long; ++i; } + } + static class C2long { byte[2] byte_ = [5, 6]; long long_ = 7; } static assert(scoped!C1long().sizeof % longAlignment == 0); static assert(scoped!C2long().sizeof % longAlignment == 0); void alignmentTest() { - // Enshure `forAlignmentOnly` field really helps - auto c1long = scoped!C1long(); + int var = 5; + auto c1long = scoped!C1long(3, var); + assert(var == 6); auto c2long = scoped!C2long(); - assert(cast(size_t)&c1long.l % longAlignment == 0); - assert(cast(size_t)&c2long.l % longAlignment == 0); + assert(cast(size_t)&c1long.long_ % longAlignment == 0); + assert(cast(size_t)&c2long.long_ % longAlignment == 0); + assert(c1long.long_ == 3 && c1long.byte_ == 4); + assert(c2long.byte_ == [5, 6] && c2long.long_ == 7); } alignmentTest(); @@ -3321,6 +3344,15 @@ unittest static assert(is(typeof(c3.foo) == immutable(int))); } +unittest +{ + class C { this(ref int val) { assert(val == 3); ++val; } } + + int val = 3; + auto s = scoped!C(val); + assert(val == 4); +} + /** Defines a simple, self-documenting yes/no flag. This makes it easy for APIs to define functions accepting flags without resorting to $(D