digitalmars.D.bugs - [Issue 18657] New: std.range and std.algorithm can't handle refRange
- d-bugmail puremagic.com (90/94) Mar 24 2018 https://issues.dlang.org/show_bug.cgi?id=18657
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