Skip to content
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

Fix issue 16246 - cannot call iota with 3 [u]bytes or 3 [u]shorts #5391

Merged
merged 2 commits into from May 15, 2017

Conversation

andralex
Copy link
Member

not much to say

@dlang-bot
Copy link
Contributor

Fix Bugzilla Description
16246 cannot call iota with 3 [u]bytes or 3 [u]shorts

@@ -5747,6 +5751,18 @@ debug @system unittest
}
}

@safe @nogc nothrow unittest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pure?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

{
{
ushort start = 0, end = 10, step = 2;
foreach (i; iota(start, end, step)) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be useful to statically assert that i is of the expected type -ushort here and ubyte below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or, just foreach(ushort i; ...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Member

@schveiguy schveiguy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, but see comments for optional improvements.

@@ -5209,6 +5209,7 @@ auto sequence(alias fun, State...)(State args)
assert(s.front != s.front); // no caching
}

// iota
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this stray comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helps quickly locating the start of the implementation.

assert(unsigned((current - pastLast) / -step) <= size_t.max);

this.pastLast = pastLast + 1;
assert(step == 0 || unsigned((current - pastLast) / -step) <= size_t.max);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

step cannot be 0 here.

assert(step != 0 && ...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, you can just remove that part of the assert.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


this.pastLast = pastLast - 1;
// Cast below can't fail because current < pastLast
this.pastLast = cast(Value) (pastLast - 1);
Copy link
Member

@schveiguy schveiguy May 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is correct, but hard to follow.

I'm wondering if it would be better to refactor this into three if branches:

if(current < pastLast && step > 0)
{
    this.step = step;
    this.current = current;
    ... // code currently inside the step > 0 branch
    this.pastLast += step; // this could probably be folded into last line
}
else if(current > pastLast && step < 0)
{
    this.step = step;
    this.current = current;
    ... // code currently inside the else branch of the inner if block
    this.pastLast += step;
}
else
{
    ... // step is 0 or current is on wrong side of pastLast
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a a toss seeing that this variant has more duplication. I'll try a variant with early returns.

Copy link
Member

@schveiguy schveiguy May 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a a toss seeing that this variant has more duplication.

True, but not duplication of conditionals that have already been checked :) There is also the inferred logic in your comment (if step > 0, then current must be < lastPast due to how the big if condition is constructed) that becomes clearer when the actual if condition is simplified.

This seems to me like the debate about combining versions and repeating the declarations. One is more DRY, but the other makes it clear which declarations are active for a specific version.

In any case, I'm OK with the code as written as it is correct and not really more confusing than the original, it just was hard to follow.

{
{
ushort start = 0, end = 10, step = 2;
foreach (i; iota(start, end, step)) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or, just foreach(ushort i; ...)

@andralex
Copy link
Member Author

Had to overhaul things a bit, representing the limit as "last" instead of "past last" because of overflows.

@schveiguy
Copy link
Member

Excellent, looks MUCH better and easier to follow the logic! Should be good to merge.

Using last instead of pastLast helps fix the issue where you want to iterate through all ubytes, for instance. I don't think there's a way to actually create such a struct, however :) iota(ubyte(0), 256) will make the thing return uints, and iota(ubyte(0), ubyte(255)) won't go through 255. But given the way the struct is now written, it would be possible to iterate all the way through 255. I'm curious if at some future point, this would be possible (maybe a compile-time config flag?), as it's one of those things that crops up periodically.

@andralex
Copy link
Member Author

Another nice thing to do with iota is have it accept a compile-time step. I.e. the Result structure would be parameterized by the step value. If the static value is zero then it's understood it's dynamic and it's a regular member etc. That way we unify a bunch of code and make it faster.

@dlang-bot dlang-bot merged commit 79edc62 into dlang:master May 15, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants