-
-
Notifications
You must be signed in to change notification settings - Fork 411
Rewrite object.dup to work with scope and copy constructors
#3474
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3435,11 +3435,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, | |
| static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~ | ||
| " to "~Unconst!T.stringof~" in dup."); | ||
|
|
||
| // wrap unsafe _dup in @trusted to preserve @safe postblit | ||
| static if (__traits(compiles, (T b) @safe { T a = b; })) | ||
| return _trustedDup!(T, Unconst!T)(a); | ||
| else | ||
| return _dup!(T, Unconst!T)(a); | ||
| return _dup!(T, Unconst!T)(a); | ||
| } | ||
|
|
||
| /// | ||
|
|
@@ -3457,11 +3453,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, | |
| @property T[] dup(T)(const(T)[] a) | ||
| if (is(const(T) : T)) | ||
| { | ||
| // wrap unsafe _dup in @trusted to preserve @safe postblit | ||
| static if (__traits(compiles, (T b) @safe { T a = b; })) | ||
| return _trustedDup!(const(T), T)(a); | ||
| else | ||
| return _dup!(const(T), T)(a); | ||
| return _dup!(const(T), T)(a); | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -3470,12 +3462,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, | |
| { | ||
| static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~ | ||
| " to immutable in idup."); | ||
|
|
||
| // wrap unsafe _dup in @trusted to preserve @safe postblit | ||
| static if (__traits(compiles, (T b) @safe { T a = b; })) | ||
| return _trustedDup!(T, immutable(T))(a); | ||
| else | ||
| return _dup!(T, immutable(T))(a); | ||
| return _dup!(T, immutable(T))(a); | ||
| } | ||
|
|
||
| /// ditto | ||
|
|
@@ -3493,34 +3480,47 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, | |
| assert(s == "abc"); | ||
| } | ||
|
|
||
| private U[] _trustedDup(T, U)(T[] a) @trusted | ||
| private U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T)) | ||
| { | ||
| return _dup!(T, U)(a); | ||
| if (__ctfe) | ||
| return _dupCtfe!(T, U)(a); | ||
|
|
||
| import core.stdc.string : memcpy; | ||
| auto arr = _d_newarrayU(typeid(T[]), a.length); | ||
| memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length); | ||
| return *cast(U[]*) &arr; | ||
| } | ||
|
|
||
| private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit | ||
| private U[] _dupCtfe(T, U)(T[] a) | ||
| { | ||
| if (__ctfe) | ||
| static if (is(T : void)) | ||
| assert(0, "Cannot dup a void[] array at compile time."); | ||
| else | ||
| { | ||
| static if (is(T : void)) | ||
| assert(0, "Cannot dup a void[] array at compile time."); | ||
| else | ||
| { | ||
| U[] res; | ||
| foreach (ref e; a) | ||
| res ~= e; | ||
| return res; | ||
| } | ||
| U[] res; | ||
| foreach (ref e; a) | ||
| res ~= e; | ||
| return res; | ||
| } | ||
| } | ||
|
|
||
| import core.stdc.string : memcpy; | ||
| private U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T)) | ||
| { | ||
| // note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch | ||
| // has the extra duty to infer _dup `@system` when the copy-constructor is `@system`. | ||
| if (__ctfe) | ||
| return _dupCtfe!(T, U)(a); | ||
|
|
||
| void[] arr = _d_newarrayU(typeid(T[]), a.length); | ||
| memcpy(arr.ptr, cast(const(void)*)a.ptr, T.sizeof * a.length); | ||
| auto res = *cast(U[]*)&arr; | ||
| import core.lifetime: copyEmplace; | ||
| U[] res = () @trusted { | ||
| auto arr = cast(U*) _d_newarrayU(typeid(T[]), a.length); | ||
| foreach (i; 0..a.length) | ||
| { | ||
| copyEmplace(a.ptr[i], arr[i]); | ||
| } | ||
| return cast(U[])(arr[0..a.length]); | ||
| } (); | ||
|
|
||
| static if (__traits(hasPostblit, T)) | ||
| _doPostblit(res); | ||
| return res; | ||
| } | ||
|
|
||
|
|
@@ -3794,20 +3794,77 @@ private void _doPostblit(T)(T[] arr) | |
| static struct Sunpure { this(this) @safe nothrow {} } | ||
| static struct Sthrow { this(this) @safe pure {} } | ||
| static struct Sunsafe { this(this) @system pure nothrow {} } | ||
| static struct Snocopy { @disable this(this); } | ||
|
|
||
| static assert( __traits(compiles, () { [].dup!Sunpure; })); | ||
| [].dup!Sunpure; | ||
| [].dup!Sthrow; | ||
| [].dup!Sunsafe; | ||
| static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); | ||
| static assert( __traits(compiles, () { [].dup!Sthrow; })); | ||
| static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); | ||
| static assert( __traits(compiles, () { [].dup!Sunsafe; })); | ||
| static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); | ||
| static assert(!__traits(compiles, () { [].dup!Snocopy; })); | ||
|
|
||
| static assert( __traits(compiles, () { [].idup!Sunpure; })); | ||
| [].idup!Sunpure; | ||
| [].idup!Sthrow; | ||
| [].idup!Sunsafe; | ||
| static assert(!__traits(compiles, () pure { [].idup!Sunpure; })); | ||
| static assert( __traits(compiles, () { [].idup!Sthrow; })); | ||
| static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; })); | ||
| static assert( __traits(compiles, () { [].idup!Sunsafe; })); | ||
| static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; })); | ||
| static assert(!__traits(compiles, () { [].idup!Snocopy; })); | ||
| } | ||
|
|
||
| @safe unittest | ||
| { | ||
| // test that the copy-constructor is called with .dup | ||
| static struct ArrElem | ||
| { | ||
| int a; | ||
| this(int a) | ||
| { | ||
| this.a = a; | ||
| } | ||
| this(ref const ArrElem) | ||
| { | ||
| a = 2; | ||
| } | ||
| this(ref ArrElem) immutable | ||
| { | ||
| a = 3; | ||
| } | ||
| } | ||
|
|
||
| auto arr = [ArrElem(1), ArrElem(1)]; | ||
|
|
||
| ArrElem[] b = arr.dup; | ||
| assert(b[0].a == 2 && b[1].a == 2); | ||
|
|
||
| immutable ArrElem[] c = arr.idup; | ||
| assert(c[0].a == 3 && c[1].a == 3); | ||
| } | ||
|
|
||
| @system unittest | ||
| { | ||
| static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} } | ||
| static struct Sthrow { this(ref const typeof(this)) @safe pure {} } | ||
| static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} } | ||
| [].dup!Sunpure; | ||
| [].dup!Sthrow; | ||
| [].dup!Sunsafe; | ||
| static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); | ||
| static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); | ||
| static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); | ||
|
|
||
| // for idup to work on structs that have copy constructors, it is necessary | ||
| // that the struct defines a copy constructor that creates immutable objects | ||
| static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} } | ||
| static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} } | ||
| static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} } | ||
| [].idup!ISunpure; | ||
| [].idup!ISthrow; | ||
| [].idup!ISunsafe; | ||
| static assert(!__traits(compiles, () pure { [].idup!ISunpure; })); | ||
| static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; })); | ||
| static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; })); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the useless Also move the instantiation out of the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call, I now just call the valid cases directly and only use the trait to test the function attributes. |
||
| } | ||
|
|
||
| @safe unittest | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is problematic because the remaining elements are not initialized to
T.initif the postblit/copy constructor throws. The GC will still destroy the entire element.https://issues.dlang.org/show_bug.cgi?id=21983
Attempted fix: #3483