This code
void main()
{
import std.algorithm.comparison;
import std.bitmanip;
import std.range.primitives;
static struct Range
{
@property bool empty() @safe const { return _arr.empty; }
@property ubyte front() @safe const { return _arr[0]; }
void popFront() @safe { _arr = _arr[1 .. $]; }
auto save() @safe { return this; }
@property size_t length() @safe const { return _arr.length; }
auto opSlice(size_t i, size_t j) @safe { return Range(_arr[i .. j]); }
this(ubyte[] arr) @safe { _arr = arr; }
ubyte[] _arr;
}
static assert(isForwardRange!Range);
static assert(!isRandomAccessRange!Range);
static assert(hasSlicing!Range);
{
auto range = Range([0, 0, 0, 0, 0, 0, 0, 0]);
range.write!uint(29110231u, 0);
assert(equal(range, cast(ubyte[])[1, 188, 47, 215, 0, 0, 0, 0]));
}
{
auto range = Range([0, 0, 0, 0, 0, 0, 0, 0]);
size_t index = 0;
range.write!ushort(261, &index);
assert(equal(range, cast(ubyte[])[1, 5, 0, 0, 0, 0, 0, 0]));
assert(index == 2);
}
}
fails to compile, giving
/usr/local/include/dmd/std/bitmanip.d(3955): Error: cannot modify expression `range[begin..end]` because it is not an lvalue
range[begin .. end] = bytes[0 .. T.sizeof];
^
/usr/local/include/dmd/std/bitmanip.d(3935): Error: template instance `std.bitmanip.write!(uint, Endian.bigEndian, Range)` error instantiating
write!(T, endianness)(range, value, &index);
^
test2.d(24): instantiated from here: `write!(uint, Endian.bigEndian, Range)`
range.write!uint(29110231u, 0);
^
/usr/local/include/dmd/std/bitmanip.d(3955): Error: cannot modify expression `range[begin..end]` because it is not an lvalue
range[begin .. end] = bytes[0 .. T.sizeof];
^
test2.d(30): Error: template instance `std.bitmanip.write!(ushort, Endian.bigEndian, Range)` error instantiating
range.write!ushort(261, &index);
Basically, write assumes that a ubyte[] can be assigned to a slice of the range. This technically could be implemented (unlike assigning a slice of a range to a ubyte[]), but it's an overload that's a royal pain to write, and the fact that no one caught the fact that peek doesn't work with ranges with slices (#11027) does make it pretty likely that no one has tried write with such a range, but since the issue with caught with read years ago but not the other two (https://issues.dlang.org/show_bug.cgi?id=17247), I'm not sure how reliable that is. Either way, the template constraint does not require such an overload, so ranges with slicing but without the appropriate assignment operator won't compile.
To further complicate things, the template constraint does not require that the elements be assignable even though the code clearly does - but since the code assigns a ubyte[] to a slice, which is not a standard range operation, hasAssignableElements wouldn't check for that anyway. So, it's theoretically possible that changing the code to use hasAssignableElements would break code if someone actually did implement the appropriate overload of opIndexAssign, though I would be very surprised if anyone has done that and used write. Now, the template constraint could be change to require assignable elements while still doing the sliced assignment in the cases where it compiles, and then the only code that should break would be code that had such an assignment operator while not allowing individual elements to be assigned, which seems like it would be kind of crazy for anyone to have implemented even if it's technically possible. So, maybe that's the proper fix, but either way, it needs to be fixed.
This code
fails to compile, giving
Basically,
writeassumes that aubyte[]can be assigned to a slice of the range. This technically could be implemented (unlike assigning a slice of a range to aubyte[]), but it's an overload that's a royal pain to write, and the fact that no one caught the fact thatpeekdoesn't work with ranges with slices (#11027) does make it pretty likely that no one has triedwritewith such a range, but since the issue with caught withreadyears ago but not the other two (https://issues.dlang.org/show_bug.cgi?id=17247), I'm not sure how reliable that is. Either way, the template constraint does not require such an overload, so ranges with slicing but without the appropriate assignment operator won't compile.To further complicate things, the template constraint does not require that the elements be assignable even though the code clearly does - but since the code assigns a
ubyte[]to a slice, which is not a standard range operation,hasAssignableElementswouldn't check for that anyway. So, it's theoretically possible that changing the code to usehasAssignableElementswould break code if someone actually did implement the appropriate overload ofopIndexAssign, though I would be very surprised if anyone has done that and usedwrite. Now, the template constraint could be change to require assignable elements while still doing the sliced assignment in the cases where it compiles, and then the only code that should break would be code that had such an assignment operator while not allowing individual elements to be assigned, which seems like it would be kind of crazy for anyone to have implemented even if it's technically possible. So, maybe that's the proper fix, but either way, it needs to be fixed.