Bug 17661 – New isInputRange rejects valid input range
Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2017-07-17T22:04:47Z
Last change time
2018-01-05T13:31:09Z
Keywords
pull
Assigned to
No Owner
Creator
hsteoh
Comments
Comment #0 by hsteoh — 2017-07-17T22:04:47Z
Code (compile with -dip25 -dip1000):
--------
import std.range.primitives;
struct C {
private S* impl;
}
struct S {
bool empty;
@property C front() return { return C(&this); }
void popFront() { }
static assert(isInputRange!S); // FAILS
}
--------
Prior to git commit 82c3371d85154b7f98b1c9187b79805a50fc59dd, the assert passes. However, the new isInputRange checks introduced by this commit causes the above breakage.
The problem is the new clause `typeof((R r) => r.front` in the new isInputRange. Because S.front returns a reference to `this` it carries the `return` annotation, so the compiler does not reject `front`. However, the check in isInputRange does not have this annotation, so the compiler rejects it as escaping a reference to `this`, thus causing isInputRange to fail to recognize S as an input range.
Comment #1 by andrei — 2017-07-17T22:51:03Z
Can you try replacing the clause is(typeof((R r) => r.front)) with
is(typeof((R r) => r.front)) || is(typeof((R r) return => r.front))
?
If that works you may want to create a PR with it. Thanks!
Comment #2 by hsteoh — 2017-07-17T23:01:31Z
Haha, I tried doing that but ran into another bug:
------
struct C {
private S* impl;
}
struct S {
bool empty;
@property C front() return { return C(&this); }
void popFront() { }
pragma(msg, typeof((S r) return => r.front));
}
------
Compiler output (dmd -dip25 -dip1000):
------
test.d(10): Error: returning r.front() escapes a reference to parameter r, perhaps annotate with return
_error_
------
Because of this, isInputRange still doesn't work, with your proposed change.
Apparently dmd fails to pick up the `return` annotation for some reason. Which is actually rather strange, because:
-------
pragma(msg, typeof((int* r) return => r));
-------
prints:
-------
int* function(return int* r) pure nothrow @nogc return @safe
-------
so obviously the syntax is correct, and at least some of the time dmd is able to process it correctly. I've no idea why it doesn't pick up the annotation and/or process it correctly when in the first code snippet above.
Comment #3 by schveiguy — 2017-07-19T14:01:00Z
(In reply to Andrei Alexandrescu from comment #1)
> is(typeof((R r) => r.front)) || is(typeof((R r) return => r.front))
I'm new to the "return" annotation, but is that right? I mean, I thought return was supposed to be an annotation on the parameter, not the function itself (which in this case is actually not a member function).
I would have expected:
(return R r) => r.front
Comment #4 by andrei — 2017-07-28T01:15:49Z
hsteoh, could you please submit those as a separate bug report for dmd and create a phobos PR using the simplest workaround you can find? That PR would close this bug, and the other bug will be for the compiler. Thanks!
Comment #5 by code — 2017-08-03T18:49:42Z
Well in, (R r) return => r.front, the return applies to the delegate context, but you're escaping a reference to the argument.
What you want to check is `(return ref R r) => r.front`.
Also rewriting your front methods to a free functions helps understanding.
C front(return ref S _this) { return C(&_this); }
Obviously allowing
(R r) => front(r)
would return a dangling reference to the value parameter.
NB, if your front methods leaked a field of the range (e.g. a pointer), you'd need a `return scope` front methods, but the `(R r) => r.front` check is fine, since the parameter `r` isn't scoped, i.e. it might leak any pointer fields.