More tricky range semantics

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Fri Jan 16 10:51:26 PST 2015


On Fri, Jan 16, 2015 at 06:34:08PM +0000, Tobias Pankrath via Digitalmars-d wrote:
> On Friday, 16 January 2015 at 18:12:03 UTC, Joseph Rushton Wakeling wrote:
> >On Friday, 16 January 2015 at 17:22:42 UTC, Tobias Pankrath wrote:
> >>Ah, now I understand you. Since copy-construction is undefined for
> >>ForwardRanges, you cannot guarantee this. Things would be better, if
> >>we had required that this(this) does the same as .save or must be
> >>@disabled.
> >
> >I'm not sure I follow what you mean by this ... ?
> 
> If you pass a forward range of type T (something that passes
> isForwardRange!T) to a function you have no guarantees on what will
> happen.  It could be:
> 
>     • compilation failure
>     • reference semantics (changes are reflected outside the function
>     • value semantics (no change are reflected outside the function)

* The range is left in an inconsistent state. :-)


> The implementer of the range can choose one. Although I tried it now,
> and @disable this(this) does prevent isForwardRange!T to pass. I don't
> know if that changed recently or is just out of line with the
> documentation/specification.

IIRC, this is because the test checks if it's possible to assign the
range to a local variable. This is quite widely used in Phobos; @disable
this(this) would make the range unusable with a *lot* of Phobos
algorithms. In fact, pretty much *all* wrapper algorithms that have to
assign the range to a field in the wrapper range.

Besides, I think this approach doesn't really address the root of the
problem, which is that the semantics of assigning a range (without using
.save) is not specified by the range API. Yet code like `auto r =
range;` is used all over the place in Phobos code, with various hidden
assumptions about what '=' does, which may or may not correspond with
reality.

Ultimately, this is also the root cause of the transient range problem:
the range API did not specify the semantics of `auto x = range.front;`.
It's simply assumed that this copies the value of range.front... which,
in a sense, it does, but what's hidden behind the '=' can be very
complex semantics that breaks the algorithm's assumptions. In this case,
it breaks because when range.front is a reference to a buffer reused by
popFront, this causes the unexpected result that calling popFront also
changes x. Had the range API specified the expected behaviour of
assigning .front to a variable, this problem would not have arisen, or
at least the possible problems would have been anticipated, instead of
transience coming to bite us from behind when we least expect it.


T

-- 
I think the conspiracy theorists are out to get us...


More information about the Digitalmars-d mailing list