Good thing we have @trusted to mark suspicious memory operations that are actually safe. When it covers a bug that @safe would have caught though, it leaves a bad taste...
First let's look at the implementation:
@property
ref Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)() @trusted
if (from <= to && to <= Types.length)
{
return *cast(typeof(return)*) &(field[from]);
}
To return a slice into the tuple, a pointer to the new first is reinterpreted as the new tuple type. This mirrors the effect of slicing an array, just that tuples are actually structs and that causes a serious problem. Consider we slice off the first element of the following tuple and compare the relative offsets of the 2nd and 3rd field respectively:
pragma(msg, Tuple!(int, bool, string)._2.offsetof - Tuple!(int, bool, string)._1.offsetof);
pragma(msg, Tuple!( bool, string)._1.offsetof - Tuple!( bool, string)._0.offsetof);
This prints:
4LU
8LU
So the relative offset of the string part moved which causes memory corruption when slice() reinterprets pointers. More visually the layout on amd64 is:
0 4 8
Tuple!(int, bool, string): int boolstring
0 4 8
Tuple!(bool, string): bool string
The memory adresses of the boolean field match, but the string moves due to its alignment constraints.
Comment #1 by thomas.bockman — 2016-02-05T10:42:29Z
Alternative fix: https://github.com/D-Programming-Language/phobos/pull/3975
This fixes the issue without changing the layout of the tuple. Please review and consider whether the changed function signature is okay.
PS: This is my first pull request on Dlang, so do let me know if I've broken any guidelines/processes :)
Comment #3 by github-bugzilla — 2017-05-17T19:34:36Z