www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 18657] New: std.range and std.algorithm can't handle refRange

https://issues.dlang.org/show_bug.cgi?id=18657

          Issue ID: 18657
           Summary: std.range and std.algorithm can't handle refRange
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: phobos
          Assignee: nobody puremagic.com
          Reporter: ag0aep6g gmail.com

Five examples:

----
void main()
{
    import std.range: refRange;
    import std.stdio;
    {
        import std.algorithm.iteration: group;
        string s = "foo";
        auto r = refRange(&s).group;
        writeln(r.save);
            /* Prints
            "[Tuple!(dchar, uint)('f', 1), Tuple!(dchar, uint)('o', 2)]". Ok.
*/
        writeln(r.save);
            /* Should print the same as the line above. Actually prints
            "[Tuple!(dchar, uint)('f', 1)]". */
    }
    {
        import std.range: chain;
        string s = "foo";
        auto r = refRange(&s).chain("bar");
        writeln(r.save); /* Should print "foobar". Actually prints "bar". */
    }
    {
        import std.range: choose;
        string s = "foo";
        auto r = choose(true, refRange(&s), "bar");
        writeln(r); /* Should print "foo". Actually prints nothing. */
    }
    {
        import std.range: cycle, take;
        string s = "foo";
        auto r = refRange(&s).cycle.take(4);
        writeln(r.save); /* Prints "foof". Ok. */
        writeln(r.save); /* Should print "foof", too. Actually prints "oofo".
*/
    }
    {
        import std.algorithm.iteration: splitter;
        string s = "foobar";
        auto r = refRange(&s).splitter!(c => c == 'b');
        writeln(r.save); /* Prints "[foo, ar]". Ok. */
        writeln(r.save);
            /* Should print the same. Actually crashes with an AssertError. */
    }
}
----

Most probably, there are more Phobos functions that can't handle refRange. I
haven't checked them all.

The root of the problem is RefRange's opAssign. Instead of just changing the
reference, it actually overwrites the referenced range. That leads to
surprising behavior:

----
void main()
{
    import std.range;
    import std.stdio;

    string s = "foo";
    auto r = refRange(&s);

    auto r2 = r;
    r2 = r2.save;
        /* Surprising: Effectively just does `s = s;` (i.e., nothing). */

    r2.popFront();
    writeln(r); /* Surprising: Prints "oo". */
}
----

Note that `r2 = r; r2 = r2.save;` is what you typically do in a postblit
function.

If RefRange's custom opAssign is removed, all the examples just work.
Unfortunately, the surprising behavior is deliberate, and not just a bug. The
docs on RefRange.opAssign say [1]:

 This does not assign the pointer of rhs to this RefRange.
 Rather it assigns the range pointed to by rhs to the range pointed
 to by this RefRange. This is because any operation on a RefRange is
 the same is if it occurred to the original range.
The issue comes down to whether RefRange should be allowed to have its funky opAssign, or if range-handling code should be allowed to assume that assignment does the obvious thing. [1] https://dlang.org/phobos/std_range.html#.RefRange.opAssign --
Mar 24 2018