Deprecate InputRange source for OutputRanges

An output range for E won't be automatically an output range for [E]
anymore. The same, an output range for [E] won't be automatically an
output range for E. Automatic E <-> [E] conversion seems to be a nice
feature at first glance, but it causes much ambiguity.

1) If I want that my output range accepts only UTF-8 strings but not
single characters (because it could be only part of a code point and
look like broken UTF-8 without the remaining code units), I can't do it
because an OutputRange(R, E) can't distinguish between char and string.

2) Here is an example from 2013:

import std.range;
import std.stdio;
Appender!(const(char)[][]) app;
put(app, "aasdf");
put(app, 'b');
writeln(app.data);

This outputs: ["aasdf", "\0"].
Whether it is a common case or not, such code just shouldn't compile.
This commit is contained in:
Eugen Wissner 2019-02-06 07:26:28 +01:00
parent bf197a6554
commit 0c8f1eb4ce
2 changed files with 23 additions and 26 deletions

View File

@ -309,7 +309,7 @@ void swap(T)(ref T a, ref T b) @trusted
* $(D_PARAM source) elements. * $(D_PARAM source) elements.
*/ */
Target copy(Source, Target)(Source source, Target target) Target copy(Source, Target)(Source source, Target target)
if (isInputRange!Source && isOutputRange!(Target, Source)) if (isInputRange!Source && isOutputRange!(Target, ElementType!Source))
in in
{ {
static if (hasLength!Source && hasLength!Target) static if (hasLength!Source && hasLength!Target)

View File

@ -798,15 +798,13 @@ template isRandomAccessRange(R)
/** /**
* Puts $(D_PARAM e) into the $(D_PARAM range). * Puts $(D_PARAM e) into the $(D_PARAM range).
* *
* $(D_PSYMBOL R) should be an output range for $(D_PARAM E). It doesn't mean * $(D_PSYMBOL R) should be an output range for $(D_PARAM E), i.e. at least one
* that everything $(D_PARAM range) is an output range for can be put into it, * of the following conditions should met:
* but only if one of the following conditions is met:
* *
* $(OL * $(OL
* $(LI $(D_PARAM R) defines a `put`-method for $(D_PARAM E))
* $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front))
* $(LI $(D_PARAM e) can be put into $(D_PARAM range) using * $(LI $(D_PARAM e) can be put into $(D_PARAM range) using
* $(D_INLINECODE range(e)) * $(D_INLINECODE range(e))
* $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front))
* ) * )
* ) * )
* *
@ -894,36 +892,28 @@ void put(R, E)(ref R range, auto ref E e)
* $(TH Scenario) * $(TH Scenario)
* ) * )
* $(TR * $(TR
* $(TD r.put(e))
* $(TD $(D_PARAM R) defines `put` for $(D_PARAM E).)
* )
* $(TR
* $(TD r.front = e)
* $(TD $(D_PARAM R) is an input range, whose element type is
* $(D_PARAM E) and `front` is an lvalue.)
* )
* $(TR
* $(TD r(e)) * $(TD r(e))
* $(TD $(D_PARAM R) defines `opCall` for $(D_PARAM E).) * $(TD $(D_PARAM R) defines `opCall` for $(D_PARAM E).)
* ) * )
* $(TR * $(TR
* $(TD for (; !e.empty; e.popFront()) r.put(e.front) $(BR) * $(TD r.front = e)
* for (; !e.empty; e.popFront(), r.popFront()) * $(TD $(D_PARAM R) is an input range with assignable elements of type
* r.front = e.front $(BR) * $(D_PARAM E).)
* for (; !e.empty; e.popFront()) r(e.front)
* )
* $(TD $(D_PARAM E) is input range, whose elements can be put into
* $(D_PARAM R) according to the rules described above in this table.
* )
* ) * )
* ) * )
* *
* Output ranges don't have element type (so $(D_PSYMBOL ElementType) returns
* $(D_KEYWORD void) when applied to an output range). It is because an output
* range can support puting differently typed elements into it.
*
* Params: * Params:
* R = The type to be tested. * R = The type to be tested.
* E = Element type should be tested for. * E = Element type should be tested for.
* *
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
* elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise. * elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL put).
*/ */
template isOutputRange(R, E) template isOutputRange(R, E)
{ {
@ -933,6 +923,11 @@ template isOutputRange(R, E)
} }
else static if (isInputRange!E) else static if (isInputRange!E)
{ {
pragma(msg, "Deprecation. An input range whose element type is "
~ "supported by the output range isn't considered itself to "
~ "be a source for such an output range. Don't rely on this "
~ "behavior and use tanya.algorithm.copy() to write one "
~ "range into another one.");
alias ET = ElementType!E; alias ET = ElementType!E;
enum bool isOutputRange = is(typeof((R r, ET e) => put(r, e))); enum bool isOutputRange = is(typeof((R r, ET e) => put(r, e)));
} }
@ -956,13 +951,16 @@ template isOutputRange(R, E)
static struct R2 static struct R2
{ {
int value; int value;
void popFront() @nogc nothrow pure @safe void popFront() @nogc nothrow pure @safe
{ {
} }
ref int front() @nogc nothrow pure @safe ref int front() @nogc nothrow pure @safe
{ {
return value; return value;
} }
bool empty() const @nogc nothrow pure @safe bool empty() const @nogc nothrow pure @safe
{ {
return true; return true;
@ -975,19 +973,18 @@ template isOutputRange(R, E)
void popFront() @nogc nothrow pure @safe void popFront() @nogc nothrow pure @safe
{ {
} }
int front() @nogc nothrow pure @safe int front() @nogc nothrow pure @safe
{ {
return 0; return 0;
} }
bool empty() const @nogc nothrow pure @safe bool empty() const @nogc nothrow pure @safe
{ {
return true; return true;
} }
} }
static assert(!isOutputRange!(R3, int)); static assert(!isOutputRange!(R3, int));
static assert(isOutputRange!(R1, R3));
static assert(isOutputRange!(R2, R3));
} }
/** /**