Bug with writeln?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Sep 6 18:52:07 UTC 2018


On Thursday, September 6, 2018 12:21:24 PM MDT Steven Schveighoffer via 
Digitalmars-d-learn wrote:
> On 9/6/18 12:55 PM, Jonathan M Davis wrote:
> > On Thursday, September 6, 2018 2:40:08 AM MDT Saurabh Das via
> > Digitalmars-d->
> > learn wrote:
> >> Is this a bug with writeln?
> >>
> >> void main()
> >> {
> >>
> >>       import std.stdio, std.range, std.algorithm;
> >>
> >>       auto a1 = sort([1,3,5,4,2]);
> >>       auto a2 = sort([9,8,9]);
> >>       auto a3 = sort([5,4,5,4]);
> >>
> >>       pragma(msg, typeof(a1));
> >>       pragma(msg, typeof(a2));
> >>       pragma(msg, typeof(a3));
> >>
> >>       auto b = [a1, a2, a3];
> >>       pragma(msg, typeof(b));
> >>
> >>       writeln("b:");
> >>       writeln(b);
> >>       writeln(b);  // <-- this one prints incorrectly
> >>
> >>       writeln("a:");
> >>       writeln(a1);
> >>       writeln(a2);
> >>       writeln(a3);
> >>
> >> }
> >>
> >> Output
> >> ======
> >>
> >> SortedRange!(int[], "a < b")
> >> SortedRange!(int[], "a < b")
> >> SortedRange!(int[], "a < b")
> >> SortedRange!(int[], "a < b")[]
> >> b:
> >> [[1, 2, 3, 4, 5], [8, 9, 9], [4, 4, 5, 5]]
> >> [[], [], []]
> >> a:
> >> [1, 2, 3, 4, 5]
> >> [8, 9, 9]
> >> [4, 4, 5, 5]
> >>
> >> The issue goes away if I cast 'b' to const before writeln. I
> >> think it is a bug, but maybe I am missing something?
> >
> > It's not a bug in writeln. Any time that a range is copied, you must not
> > do _anything_ else with the original unless copying it is equivalent to
> > calling save on it, because the semantics of copying a range are
> > unspecified. They vary wildly depending on the range type (e.g. copying
> > a dynamic array is equivalent to calling save, but copying a class
> > reference is not). When you pass the range to writeln, you must assumed
> > that it may have been consumed. And since you have range of ranges, you
> > must assume that the ranges that are contained may have been consumed.
> > If you want to pass them to writeln and then do anything else with
> > them, then you'll need to call save on every range involved (which is a
> > bit of a pain with a range of ranges, but it's necessary all the same).
>
> This is not necessarily true. It depends how the sub-ranges are returned.
>
> The bug is that formattedWrite takes ranges sometimes by ref, sometimes
> not.
>
> formattedWrite should call save on a forward range whenever it makes a
> copy, and it doesn't.
>
> Case in point, it doesn't matter if you call writeln(b.save), the same
> thing happens.

That's still not a bug in formattedWrite. save only duplicates the
outer-most range. And since writeln will ultimately iterate through the
inner ranges - which weren't saved - you end up with them being consumed.
When you're passing a range of ranges to a function, you need to recursively
save them if you don't want the inner ranges in the original range to be
consumed. Regardless of what formattedWrite does, it's a general issue with
any function that you pass a range of ranges. It comes right back to the
same issue of the semantics of copying ranges being unspecified and that you
therefore must always use save on any ranges involved if you want to then
use those ranges after having passed them to a function or copy them doing
anything else. It's that much more annoying when you're dealing with a range
of ranges rather than a range of something else, but the issue is the same.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list