Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 102 additions & 187 deletions std/range/primitives.d
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,12 @@ Params:
Returns:
true if R is an InputRange, false if not
*/
template isInputRange(R)
{
enum bool isInputRange = is(typeof(
(inout int = 0)
{
R r = R.init; // can define a range object
if (r.empty) {} // can test for empty
r.popFront; // can invoke popFront()
auto h = r.front; // can get the front of the range
}));
}
enum bool isInputRange(R) =
is(typeof(R.init) == R)
&& is(ReturnType!((R r) => r.empty) == bool)
&& is(typeof((R r) => r.front))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line fails if the range being tested has a front method annotated with return.

&& !is(ReturnType!((R r) => r.front) == void)
&& is(typeof((R r) => r.popFront));

///
@safe unittest
Expand All @@ -189,6 +184,40 @@ template isInputRange(R)
static assert( isInputRange!(char[]));
static assert(!isInputRange!(char[4]));
static assert( isInputRange!(inout(int)[]));

static struct NotDefaultConstructible
{
@disable this();
void popFront();
@property bool empty();
@property int front();
}
static assert( isInputRange!NotDefaultConstructible);

static struct NotDefaultConstructibleOrCopyable
{
@disable this();
@disable this(this);
void popFront();
@property bool empty();
@property int front();
}
static assert(isInputRange!NotDefaultConstructibleOrCopyable);

static struct Frontless
{
void popFront();
@property bool empty();
}
static assert(!isInputRange!Frontless);

static struct VoidFront
{
void popFront();
@property bool empty();
void front();
}
static assert(!isInputRange!VoidFront);
}

/+
Expand Down Expand Up @@ -691,16 +720,8 @@ are:
2: if $(D E) is a non $(empty) $(D InputRange), then placing $(D e) is
guaranteed to not overflow the range.
+/
package(std) template isNativeOutputRange(R, E)
{
enum bool isNativeOutputRange = is(typeof(
(inout int = 0)
{
R r = void;
E e;
doPut(r, e);
}));
}
package(std) enum bool isNativeOutputRange(R, E) =
is(typeof(doPut(lvalueOf!R, lvalueOf!E)));

@safe unittest
{
Expand All @@ -715,21 +736,14 @@ package(std) template isNativeOutputRange(R, E)
if (!r.empty)
put(r, [1, 2]); //May actually error out.
}

/++
Returns $(D true) if $(D R) is an output range for elements of type
$(D E). An output range is defined functionally as a range that
supports the operation $(D put(r, e)) as defined above.
+/
template isOutputRange(R, E)
{
enum bool isOutputRange = is(typeof(
(inout int = 0)
{
R r = R.init;
E e = E.init;
put(r, e);
}));
}
enum bool isOutputRange(R, E) =
is(typeof(put(lvalueOf!R, lvalueOf!E)));

///
@safe unittest
Expand Down Expand Up @@ -791,19 +805,8 @@ are the same as for an input range, with the additional requirement
that backtracking must be possible by saving a copy of the range
object with $(D save) and using it later.
*/
template isForwardRange(R)
{
enum bool isForwardRange = isInputRange!R && is(typeof(
(inout int = 0)
{
R r1 = R.init;
// NOTE: we cannot check typeof(r1.save) directly
// because typeof may not check the right type there, and
// because we want to ensure the range can be copied.
auto s1 = r1.save;
static assert(is(typeof(s1) == R));
}));
}
enum bool isForwardRange(R) = isInputRange!R
&& is(ReturnType!((R r) => r.save) == R);

///
@safe unittest
Expand Down Expand Up @@ -841,18 +844,9 @@ $(UL $(LI $(D r.back) returns (possibly a reference to) the last
element in the range. Calling $(D r.back) is allowed only if calling
$(D r.empty) has, or would have, returned $(D false).))
*/
template isBidirectionalRange(R)
{
enum bool isBidirectionalRange = isForwardRange!R && is(typeof(
(inout int = 0)
{
R r = R.init;
r.popBack;
auto t = r.back;
auto w = r.front;
static assert(is(typeof(t) == typeof(w)));
}));
}
enum bool isBidirectionalRange(R) = isForwardRange!R
&& is(typeof((R r) => r.popBack))
&& is(ReturnType!((R r) => r.back) == ElementType!R);

///
@safe unittest
Expand Down Expand Up @@ -911,29 +905,14 @@ isRandomAccessRange) yields $(D false) for them because they use
variable-length encodings (UTF-8 and UTF-16 respectively). These types
are bidirectional ranges only.
*/
template isRandomAccessRange(R)
{
enum bool isRandomAccessRange = is(typeof(
(inout int = 0)
{
static assert(isBidirectionalRange!R ||
isForwardRange!R && isInfinite!R);
R r = R.init;
auto e = r[1];
auto f = r.front;
static assert(is(typeof(e) == typeof(f)));
static assert(!isNarrowString!R);
static assert(hasLength!R || isInfinite!R);

static if (is(typeof(r[$])))
{
static assert(is(typeof(f) == typeof(r[$])));

static if (!isInfinite!R)
static assert(is(typeof(f) == typeof(r[$ - 1])));
}
}));
}
enum bool isRandomAccessRange(R) =
is(typeof(lvalueOf!R[1]) == ElementType!R)
&& !isNarrowString!R
&& isForwardRange!R
&& (isBidirectionalRange!R || isInfinite!R)
&& (hasLength!R || isInfinite!R)
&& (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1]))
|| is(typeof(lvalueOf!R[$ - 1]) == ElementType!R));

///
@safe unittest
Expand Down Expand Up @@ -1065,20 +1044,13 @@ static if (isRandomAccessRange!R)
static assert(is(typeof(moveAt(r, 0)) == E));
----
*/
template hasMobileElements(R)
{
enum bool hasMobileElements = isInputRange!R && is(typeof(
(inout int = 0)
{
alias E = ElementType!R;
R r = R.init;
static assert(is(typeof(moveFront(r)) == E));
static if (isBidirectionalRange!R)
static assert(is(typeof(moveBack(r)) == E));
static if (isRandomAccessRange!R)
static assert(is(typeof(moveAt(r, 0)) == E));
}));
}
enum bool hasMobileElements(R) =
isInputRange!R
&& is(typeof(moveFront(lvalueOf!R)) == ElementType!R)
&& (!isBidirectionalRange!R
|| is(typeof(moveBack(lvalueOf!R)) == ElementType!R))
&& (!isRandomAccessRange!R
|| is(typeof(moveAt(lvalueOf!R, 0)) == ElementType!R));

///
@safe unittest
Expand Down Expand Up @@ -1271,14 +1243,12 @@ static if (isRandomAccessRange!R) swap(r[0], r.front);
template hasSwappableElements(R)
{
import std.algorithm.mutation : swap;
enum bool hasSwappableElements = isInputRange!R && is(typeof(
(inout int = 0)
{
R r = R.init;
swap(r.front, r.front);
static if (isBidirectionalRange!R) swap(r.back, r.front);
static if (isRandomAccessRange!R) swap(r[0], r.front);
}));
enum bool hasSwappableElements = isInputRange!R
&& is(typeof((ref R r) => swap(r.front, r.front)))
&& (!isBidirectionalRange!R
|| is(typeof((ref R r) => swap(r.back, r.front))))
&& (!isRandomAccessRange!R
|| is(typeof((ref R r) => swap(r[0], r.front))));
}

///
Expand Down Expand Up @@ -1308,17 +1278,12 @@ static if (isBidirectionalRange!R) r.back = r.front;
static if (isRandomAccessRange!R) r[0] = r.front;
----
*/
template hasAssignableElements(R)
{
enum bool hasAssignableElements = isInputRange!R && is(typeof(
(inout int = 0)
{
R r = R.init;
r.front = r.front;
static if (isBidirectionalRange!R) r.back = r.front;
static if (isRandomAccessRange!R) r[0] = r.front;
}));
}
enum bool hasAssignableElements(R) = isInputRange!R
&& is(typeof(lvalueOf!R.front = lvalueOf!R.front))
&& (!isBidirectionalRange!R
|| is(typeof(lvalueOf!R.back = lvalueOf!R.back)))
&& (!isRandomAccessRange!R
|| is(typeof(lvalueOf!R[0] = lvalueOf!R.front)));

///
@safe unittest
Expand Down Expand Up @@ -1347,19 +1312,12 @@ static if (isBidirectionalRange!R) passByRef(r.back);
static if (isRandomAccessRange!R) passByRef(r[0]);
----
*/
template hasLvalueElements(R)
{
enum bool hasLvalueElements = isInputRange!R && is(typeof(
(inout int = 0)
{
void checkRef(ref ElementType!R stuff);
R r = R.init;

checkRef(r.front);
static if (isBidirectionalRange!R) checkRef(r.back);
static if (isRandomAccessRange!R) checkRef(r[0]);
}));
}
enum bool hasLvalueElements(R) = isInputRange!R
&& is(typeof(((ref x) => x)(lvalueOf!R.front)))
&& (!isBidirectionalRange!R
|| is(typeof(((ref x) => x)(lvalueOf!R.back))))
&& (!isRandomAccessRange!R
|| is(typeof(((ref x) => x)(lvalueOf!R[0]))));

///
@safe unittest
Expand Down Expand Up @@ -1502,76 +1460,33 @@ original range (they both return the same type for infinite ranges). However,
when using $(D opDollar), the result of $(D opSlice) must be that of the
original range type.

The following code must compile for $(D hasSlicing) to be $(D true):
The following expression must be true for `hasSlicing` to be `true`:

----
R r = void;

static if (isInfinite!R)
typeof(take(r, 1)) s = r[1 .. 2];
else
{
static assert(is(typeof(r[1 .. 2]) == R));
R s = r[1 .. 2];
}

s = r[1 .. 2];

static if (is(typeof(r[0 .. $])))
{
static assert(is(typeof(r[0 .. $]) == R));
R t = r[0 .. $];
t = r[0 .. $];

static if (!isInfinite!R)
isForwardRange!R
&& !isNarrowString!R
&& is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
&& (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
&& (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
&& (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R
|| is(typeof(lvalueOf!R[0 .. $ - 1]) == R))
&& is(typeof((ref R r)
{
static assert(is(typeof(r[0 .. $ - 1]) == R));
R u = r[0 .. $ - 1];
u = r[0 .. $ - 1];
}
}

static assert(isForwardRange!(typeof(r[1 .. 2])));
static assert(hasLength!(typeof(r[1 .. 2])));
static assert(isForwardRange!(typeof(r[1 .. 2])));
}));
----
*/
template hasSlicing(R)
{
enum bool hasSlicing = isForwardRange!R && !isNarrowString!R && is(typeof(
(inout int = 0)
enum bool hasSlicing(R) = isForwardRange!R
&& !isNarrowString!R
&& is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
&& (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
&& (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
&& (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R
|| is(typeof(lvalueOf!R[0 .. $ - 1]) == R))
&& is(typeof((ref R r)
{
R r = R.init;

static if (isInfinite!R)
{
typeof(r[1 .. 1]) s = r[1 .. 2];
}
else
{
static assert(is(typeof(r[1 .. 2]) == R));
R s = r[1 .. 2];
}

s = r[1 .. 2];

static if (is(typeof(r[0 .. $])))
{
static assert(is(typeof(r[0 .. $]) == R));
R t = r[0 .. $];
t = r[0 .. $];

static if (!isInfinite!R)
{
static assert(is(typeof(r[0 .. $ - 1]) == R));
R u = r[0 .. $ - 1];
u = r[0 .. $ - 1];
}
}

static assert(isForwardRange!(typeof(r[1 .. 2])));
static assert(hasLength!(typeof(r[1 .. 2])));
}));
}

///
@safe unittest
Expand Down
1 change: 1 addition & 0 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -7943,4 +7943,5 @@ enum isCopyable(S) = is(typeof(

static assert(isCopyable!C1);
static assert(isCopyable!int);
static assert(isCopyable!(int[]));
}