Merge pull request #2631 from nordlow/inout-array-range

Fix constness of array.d
This commit is contained in:
JakobOvrum 2015-01-29 15:38:53 +09:00
commit 224238a854

View file

@ -22,9 +22,161 @@ module std.container.array;
import std.range.primitives; import std.range.primitives;
import std.traits; import std.traits;
import core.exception : RangeError;
import std.algorithm : move;
public import std.container.util; public import std.container.util;
private struct RangeT(A)
{
/* Workaround for Issue 13629 at https://issues.dlang.org/show_bug.cgi?id=13629
See also: http://forum.dlang.org/thread/vbmwhzvawhnkoxrhbnyb@forum.dlang.org?page=1
*/
private A[1] _outer_;
private @property ref inout(A) _outer() inout { return _outer_[0]; }
private size_t _a, _b;
/* E is different from T when A is more restrictively qualified than T:
immutable(Array!int) => T == int, E = immutable(int) */
alias E = typeof(_outer_[0]._data._payload[0]);
private this(ref A data, size_t a, size_t b)
{
_outer_ = data;
_a = a;
_b = b;
}
@property RangeT save()
{
return this;
}
@property bool empty() @safe pure nothrow const
{
return _a >= _b;
}
@property size_t length() @safe pure nothrow const
{
return _b - _a;
}
alias opDollar = length;
@property ref inout(E) front() inout
{
version (assert) if (empty) throw new RangeError();
return _outer[_a];
}
@property ref inout(E) back() inout
{
version (assert) if (empty) throw new RangeError();
return _outer[_b - 1];
}
void popFront() @safe pure nothrow
{
version (assert) if (empty) throw new RangeError();
++_a;
}
void popBack() @safe pure nothrow
{
version (assert) if (empty) throw new RangeError();
--_b;
}
static if (isMutable!A)
{
E moveFront()
{
version (assert) if (empty || _a >= _outer.length) throw new RangeError();
return move(_outer._data._payload[_a]);
}
E moveBack()
{
version (assert) if (empty || _b > _outer.length) throw new RangeError();
return move(_outer._data._payload[_b - 1]);
}
E moveAt(size_t i)
{
version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError();
return move(_outer._data._payload[_a + i]);
}
}
ref inout(E) opIndex(size_t i) inout
{
version (assert) if (_a + i >= _b) throw new RangeError();
return _outer[_a + i];
}
RangeT opSlice()
{
return typeof(return)(_outer, _a, _b);
}
RangeT opSlice(size_t i, size_t j)
{
version (assert) if (i > j || _a + j > _b) throw new RangeError();
return typeof(return)(_outer, _a + i, _a + j);
}
RangeT!(const(A)) opSlice() const
{
return typeof(return)(_outer, _a, _b);
}
RangeT!(const(A)) opSlice(size_t i, size_t j) const
{
version (assert) if (i > j || _a + j > _b) throw new RangeError();
return typeof(return)(_outer, _a + i, _a + j);
}
static if (isMutable!A)
{
void opSliceAssign(E value)
{
version (assert) if (_b > _outer.length) throw new RangeError();
_outer[_a .. _b] = value;
}
void opSliceAssign(E value, size_t i, size_t j)
{
version (assert) if (_a + j > _b) throw new RangeError();
_outer[_a + i .. _a + j] = value;
}
void opSliceUnary(string op)()
if (op == "++" || op == "--")
{
version (assert) if (_b > _outer.length) throw new RangeError();
mixin(op~"_outer[_a .. _b];");
}
void opSliceUnary(string op)(size_t i, size_t j)
if (op == "++" || op == "--")
{
version (assert) if (_a + j > _b) throw new RangeError();
mixin(op~"_outer[_a + i .. _a + j];");
}
void opSliceOpAssign(string op)(E value)
{
version (assert) if (_b > _outer.length) throw new RangeError();
mixin("_outer[_a .. _b] "~op~"= value;");
}
void opSliceOpAssign(string op)(E value, size_t i, size_t j)
{
version (assert) if (_a + j > _b) throw new RangeError();
mixin("_outer[_a + i .. _a + j] "~op~"= value;");
}
}
}
/** /**
Array type with deterministic control of memory. The memory allocated Array type with deterministic control of memory. The memory allocated
@ -50,9 +202,8 @@ if (!is(Unqual!T == bool))
import core.stdc.string; import core.stdc.string;
import core.memory; import core.memory;
import core.exception : RangeError;
import std.algorithm : initializeAll, move, copy; import std.algorithm : initializeAll, copy;
import std.exception : enforce; import std.exception : enforce;
import std.typecons : RefCounted, RefCountedAutoInitialize; import std.typecons : RefCounted, RefCountedAutoInitialize;
@ -259,133 +410,14 @@ Comparison for equality.
} }
/** /**
Defines the container's primary range, which is a random-access range. Defines the container's primary range, which is a random-access range.
*/
static struct Range
{
private Array _outer;
private size_t _a, _b;
private this(ref Array data, size_t a, size_t b) ConstRange is a variant with const elements.
{ ImmutableRange is a variant with immutable elements.
_outer = data; */
_a = a; alias Range = RangeT!Array;
_b = b; alias ConstRange = RangeT!(const Array); /// ditto
} alias ImmutableRange = RangeT!(immutable Array); /// ditto
@property Range save()
{
return this;
}
@property bool empty() @safe pure nothrow const
{
return _a >= _b;
}
@property size_t length() @safe pure nothrow const
{
return _b - _a;
}
alias opDollar = length;
@property ref T front()
{
version (assert) if (empty) throw new RangeError();
return _outer[_a];
}
@property ref T back()
{
version (assert) if (empty) throw new RangeError();
return _outer[_b - 1];
}
void popFront() @safe pure nothrow
{
version (assert) if (empty) throw new RangeError();
++_a;
}
void popBack() @safe pure nothrow
{
version (assert) if (empty) throw new RangeError();
--_b;
}
T moveFront()
{
version (assert) if (empty || _a >= _outer.length) throw new RangeError();
return move(_outer._data._payload[_a]);
}
T moveBack()
{
version (assert) if (empty || _b > _outer.length) throw new RangeError();
return move(_outer._data._payload[_b - 1]);
}
T moveAt(size_t i)
{
version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError();
return move(_outer._data._payload[_a + i]);
}
ref T opIndex(size_t i)
{
version (assert) if (_a + i >= _b) throw new RangeError();
return _outer[_a + i];
}
typeof(this) opSlice()
{
return typeof(this)(_outer, _a, _b);
}
typeof(this) opSlice(size_t i, size_t j)
{
version (assert) if (i > j || _a + j > _b) throw new RangeError();
return typeof(this)(_outer, _a + i, _a + j);
}
void opSliceAssign(T value)
{
version (assert) if (_b > _outer.length) throw new RangeError();
_outer[_a .. _b] = value;
}
void opSliceAssign(T value, size_t i, size_t j)
{
version (assert) if (_a + j > _b) throw new RangeError();
_outer[_a + i .. _a + j] = value;
}
void opSliceUnary(string op)()
if(op == "++" || op == "--")
{
version (assert) if (_b > _outer.length) throw new RangeError();
mixin(op~"_outer[_a .. _b];");
}
void opSliceUnary(string op)(size_t i, size_t j)
if(op == "++" || op == "--")
{
version (assert) if (_a + j > _b) throw new RangeError();
mixin(op~"_outer[_a + i .. _a + j];");
}
void opSliceOpAssign(string op)(T value)
{
version (assert) if (_b > _outer.length) throw new RangeError();
mixin("_outer[_a .. _b] "~op~"= value;");
}
void opSliceOpAssign(string op)(T value, size_t i, size_t j)
{
version (assert) if (_a + j > _b) throw new RangeError();
mixin("_outer[_a + i .. _a + j] "~op~"= value;");
}
}
/** /**
Duplicates the container. The elements themselves are not transitively Duplicates the container. The elements themselves are not transitively
@ -472,7 +504,15 @@ Complexity: $(BIGOH 1)
*/ */
Range opSlice() Range opSlice()
{ {
return Range(this, 0, length); return typeof(return)(this, 0, length);
}
ConstRange opSlice() const
{
return typeof(return)(this, 0, length);
}
ImmutableRange opSlice() immutable
{
return typeof(return)(this, 0, length);
} }
/** /**
@ -482,11 +522,21 @@ index $(D a) up to (excluding) index $(D b).
Precondition: $(D a <= b && b <= length) Precondition: $(D a <= b && b <= length)
Complexity: $(BIGOH 1) Complexity: $(BIGOH 1)
*/ */
Range opSlice(size_t i, size_t j) Range opSlice(size_t i, size_t j)
{ {
version (assert) if (i > j || j > length) throw new RangeError(); version (assert) if (i > j || j > length) throw new RangeError();
return Range(this, i, j); return typeof(return)(this, i, j);
}
ConstRange opSlice(size_t i, size_t j) const
{
version (assert) if (i > j || j > length) throw new RangeError();
return typeof(return)(this, i, j);
}
ImmutableRange opSlice(size_t i, size_t j) immutable
{
version (assert) if (i > j || j > length) throw new RangeError();
return typeof(return)(this, i, j);
} }
/** /**
@ -496,14 +546,14 @@ Precondition: $(D !empty)
Complexity: $(BIGOH 1) Complexity: $(BIGOH 1)
*/ */
@property ref T front() @property ref inout(T) front() inout
{ {
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
return _data._payload[0]; return _data._payload[0];
} }
/// ditto /// ditto
@property ref T back() @property ref inout(T) back() inout
{ {
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
return _data._payload[$ - 1]; return _data._payload[$ - 1];
@ -516,7 +566,7 @@ Precondition: $(D i < length)
Complexity: $(BIGOH 1) Complexity: $(BIGOH 1)
*/ */
ref T opIndex(size_t i) ref inout(T) opIndex(size_t i) inout
{ {
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
return _data._payload[i]; return _data._payload[i];
@ -546,15 +596,15 @@ Complexity: $(BIGOH slice.length)
/// ditto /// ditto
void opSliceUnary(string op)() void opSliceUnary(string op)()
if(op == "++" || op == "--") if (op == "++" || op == "--")
{ {
if(!_data.refCountedStore.isInitialized) return; if (!_data.refCountedStore.isInitialized) return;
mixin(op~"_data._payload[];"); mixin(op~"_data._payload[];");
} }
/// ditto /// ditto
void opSliceUnary(string op)(size_t i, size_t j) void opSliceUnary(string op)(size_t i, size_t j)
if(op == "++" || op == "--") if (op == "++" || op == "--")
{ {
auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
mixin(op~"slice[i .. j];"); mixin(op~"slice[i .. j];");
@ -563,7 +613,7 @@ Complexity: $(BIGOH slice.length)
/// ditto /// ditto
void opSliceOpAssign(string op)(T value) void opSliceOpAssign(string op)(T value)
{ {
if(!_data.refCountedStore.isInitialized) return; if (!_data.refCountedStore.isInitialized) return;
mixin("_data._payload[] "~op~"= value;"); mixin("_data._payload[] "~op~"= value;");
} }
@ -898,6 +948,27 @@ unittest
assert(a.length == 3); assert(a.length == 3);
} }
unittest
{
const Array!int a = [1, 2];
assert(a[0] == 1);
assert(a.front == 1);
assert(a.back == 2);
static assert(!__traits(compiles, { a[0] = 1; }));
static assert(!__traits(compiles, { a.front = 1; }));
static assert(!__traits(compiles, { a.back = 1; }));
auto r = a[];
size_t i;
foreach (e; r)
{
assert(e == i + 1);
i++;
}
}
unittest unittest
{ {
// REG https://issues.dlang.org/show_bug.cgi?id=13621 // REG https://issues.dlang.org/show_bug.cgi?id=13621
@ -1242,6 +1313,41 @@ unittest //6998-2
assert(c.i == 42); //fails assert(c.i == 42); //fails
} }
unittest
{
static assert(is(Array!int.Range));
static assert(is(Array!int.ConstRange));
}
unittest // const/immutable Array and Ranges
{
static void test(A, R, E, S)()
{
A a;
R r = a[];
assert(r.empty);
assert(r.length == 0);
static assert(is(typeof(r.front) == E));
static assert(is(typeof(r.back) == E));
static assert(is(typeof(r[0]) == E));
static assert(is(typeof(r[]) == S));
static assert(is(typeof(r[0 .. 0]) == S));
}
alias A = Array!int;
test!(A, A.Range, int, A.Range);
test!(A, const A.Range, const int, A.ConstRange);
test!(const A, A.ConstRange, const int, A.ConstRange);
test!(const A, const A.ConstRange, const int, A.ConstRange);
test!(immutable A, A.ImmutableRange, immutable int, A.ImmutableRange);
test!(immutable A, const A.ImmutableRange, immutable int, A.ImmutableRange);
test!(immutable A, immutable A.ImmutableRange, immutable int,
A.ImmutableRange);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Array!bool // Array!bool