-
-
Notifications
You must be signed in to change notification settings - Fork 705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
isForwardRange doesn't work with alias range this or inheritance #10376
Comments
dlang-bot commented on 2019-06-28T07:19:10Z@shove70 created dlang/phobos pull request #7095 "Fix issue 20009 - isForwardRange doesn't work with alias range this o…" fixing this issue:
- Fix issue 20009 - isForwardRange doesn't work with alias range this or inheritance
https://github.com/dlang/phobos/pull/7095 |
shove commented on 2019-06-29T05:01:15Z(In reply to Ali Ak from comment #0)
> struct FR {
> bool empty = true;
> void popFront() {}
> auto front() { return 0; }
> FR save() { return this; }
> }
>
> struct S {
> FR range;
> alias range this;
> }
>
> pragma(msg, isInputRange!S); // true
> pragma(msg, isForwardRange!S); // false
>
> It's because isForwardRange is defined as:
>
> isInputRange!R && is(ReturnType!((R r) => r.save) == R);
>
> But it should be:
>
> isInputRange!R && is(R : ReturnType!((R r) => r.save));
>
> The same thing will happen if you try an inherit from an interface that is a
> forward range, and if the save function returns the base class it will fail.
Since struct S inherits from struct FR, I think it should have the characteristics of ForwardRange too.
But unfortunately, after I do it according to your idea, although struct S passed the isForwardRange test, it caused a lot of compilation errors in the entire Phobos, including function code and assertions in unit tests. The reasons for this include at least the following:
1. is(T1 : T2) is somewhat different than what I have understood before:
is(R!(int) : int[]) will pass the test, obviously this is not what is expected here.
2. Too many templates and APIs assume that the return type of save() is exactly equal to itself, and cannot be equal to the base class. It is equal to the base class that will overturn these assumptions and cause the program to fail or assert the failure. Such as:
std.std.algorithm.searching.d line 3359:
Range least = range.save;
Obviously, the base type cannot be assigned to a derived type, and this assumption has limited the expectations of this issue.
Therefore, if you want isForwardRange to support derived types, then Phobos has too many places to change, and the workload is a bit large.
It might be a good idea to add a new template to the existing one that is specifically designed to test derived types with ForwardRange attributes:
template isDerivedForwardRange(R)
{
static if (isInputRange!R && is(typeof((R r) => r.save) RT))
enum bool isForwardRange = !is(R == ReturnType!RT) && is(R : ReturnType!RT);
else
enum bool isForwardRange = false;
}
But I don't know if it has practical use. |
ali.akhtarzada commented on 2019-07-08T08:39:26ZWhy mark this invalid? Are interfaces and alias this'd ranges not supposed to work? |
shove commented on 2019-07-08T09:21:32Z(In reply to Ali Ak from comment #3)
> Why mark this invalid? Are interfaces and alias this'd ranges not supposed
> to work?
Misread the title and misoperate it. I'm sorry. |
|
In general, testing for implicit conversions with template constraints is fraught with bugs. In particular, if a type passes a template constraint due to an implicit conversion, but the templated function does not then force the conversion, there can be compilation errors at best (due to the type not actually working with the code even if it would have if it had been converted), or worse it can result in silently incorrect behavior due to the fact that the implicit conversion happens implicitly in some parts of the code but not others (e.g. it could happen when And to make matters worse, in some cases, even if the conversion is forced within the function, it's an So, as a general rule, it's a huge footgun to have template constraints allow implicit conversions, and just like range-based functions do not normally implicitly accept static arrays (rather, you have to slice them to get a range), types which are not themselves ranges should require that they be explicitly converted to a range at the call site. To do otherwise is to open the door to subtle bugs. So, IMHO, it would be a huge mistake to change So, Phobos 3 will most definitely not be allowing implicit conversions for traits like So, if a type is a range on its own, it's a range, whereas if it can be implicitly converted to be a range but isn't itself a range, code that wants to use it as a range is going to need to force the conversion. And that's not going to change, because it would risk far too many bugs in general even if it happens to work in some cases. |
ali.akhtarzada reported this on 2019-06-27T21:24:42Z
Transfered from https://issues.dlang.org/show_bug.cgi?id=20009
CC List
Description
struct FR { bool empty = true; void popFront() {} auto front() { return 0; } FR save() { return this; } } struct S { FR range; alias range this; } pragma(msg, isInputRange!S); // true pragma(msg, isForwardRange!S); // false It's because isForwardRange is defined as: isInputRange!R && is(ReturnType!((R r) => r.save) == R); But it should be: isInputRange!R && is(R : ReturnType!((R r) => r.save)); The same thing will happen if you try an inherit from an interface that is a forward range, and if the save function returns the base class it will fail.The text was updated successfully, but these errors were encountered: