diff --git a/std/range/primitives.d b/std/range/primitives.d index 7dc0d018a02..99064ba73ec 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -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)) + && !is(ReturnType!((R r) => r.front) == void) + && is(typeof((R r) => r.popFront)); /// @safe unittest @@ -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); } /+ @@ -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 { @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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)))); } /// @@ -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 @@ -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 @@ -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 diff --git a/std/traits.d b/std/traits.d index d32a5e967ec..5b8700cca80 100644 --- a/std/traits.d +++ b/std/traits.d @@ -7943,4 +7943,5 @@ enum isCopyable(S) = is(typeof( static assert(isCopyable!C1); static assert(isCopyable!int); + static assert(isCopyable!(int[])); }