From 6ff81f14057530484249dd4055e19c996b0485a7 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 14 May 2017 08:19:20 -0400 Subject: [PATCH 1/2] Fix issue 16246 - cannot call iota with 3 [u]bytes or 3 [u]shorts --- std/range/package.d | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 1582754713c..75fbdf8cf28 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -5209,6 +5209,7 @@ auto sequence(alias fun, State...)(State args) assert(s.front != s.front); // no caching } +// iota /** Construct a range of values that span the given starting and stopping values. @@ -5277,24 +5278,23 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) this(Value current, Value pastLast, StepType step) { - if ((current < pastLast && step >= 0) || - (current > pastLast && step <= 0)) + if ((current < pastLast && step > 0) || + (current > pastLast && step < 0)) { this.step = step; this.current = current; if (step > 0) { assert(unsigned((pastLast - current) / step) <= size_t.max); - - this.pastLast = pastLast - 1; + // Cast below can't fail because current < pastLast + this.pastLast = cast(Value) (pastLast - 1); this.pastLast -= (this.pastLast - current) % step; } else { - if (step < 0) - assert(unsigned((current - pastLast) / -step) <= size_t.max); - - this.pastLast = pastLast + 1; + assert(step == 0 || unsigned((current - pastLast) / -step) <= size_t.max); + // Cast below can't fail because current > pastLast + this.pastLast = cast(Value) (pastLast + 1); this.pastLast += (current - this.pastLast) % -step; } this.pastLast += step; @@ -5311,7 +5311,11 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) @property inout(Value) front() inout { assert(!empty); return current; } void popFront() { assert(!empty); current += step; } - @property inout(Value) back() inout { assert(!empty); return pastLast - step; } + @property inout(Value) back() inout + { + assert(!empty); + return cast(Value) (pastLast - step); + } void popBack() { assert(!empty); pastLast -= step; } @property auto save() { return this; } @@ -5747,6 +5751,18 @@ debug @system unittest } } +@safe @nogc nothrow unittest +{ + { + ushort start = 0, end = 10, step = 2; + foreach (i; iota(start, end, step)) {} + } + { + ubyte start = 0, end = 10, step = 2; + foreach (i; iota(start, end, step)) {} + } +} + /* Generic overload that handles arbitrary types that support arithmetic * operations. * From 74514bc7e5fed4dccec05e55253e70f1e6c06cc9 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 14 May 2017 12:11:41 -0400 Subject: [PATCH 2/2] Improve behavior on overflow --- std/range/package.d | 95 ++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 75fbdf8cf28..bec3091f95c 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -5273,50 +5273,57 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) static struct Result { - private Value current, pastLast; - private StepType step; + private Value current, last; + private StepType step; // by convention, 0 if range is empty this(Value current, Value pastLast, StepType step) { - if ((current < pastLast && step > 0) || - (current > pastLast && step < 0)) + if (current < pastLast && step > 0) { - this.step = step; - this.current = current; - if (step > 0) - { - assert(unsigned((pastLast - current) / step) <= size_t.max); - // Cast below can't fail because current < pastLast - this.pastLast = cast(Value) (pastLast - 1); - this.pastLast -= (this.pastLast - current) % step; - } - else - { - assert(step == 0 || unsigned((current - pastLast) / -step) <= size_t.max); - // Cast below can't fail because current > pastLast - this.pastLast = cast(Value) (pastLast + 1); - this.pastLast += (current - this.pastLast) % -step; - } - this.pastLast += step; + // Iterating upward + assert(unsigned((pastLast - current) / step) <= size_t.max); + // Cast below can't fail because current < pastLast + this.last = cast(Value) (pastLast - 1); + this.last -= unsigned(this.last - current) % step; + } + else if (current > pastLast && step < 0) + { + // Iterating downward + assert(unsigned((current - pastLast) / -step) <= size_t.max); + // Cast below can't fail because current > pastLast + this.last = cast(Value) (pastLast + 1); + this.last += unsigned(current - this.last) % -step; } else { // Initialize an empty range - this.current = this.pastLast = current; - this.step = 1; + this.step = 0; + return; } + this.step = step; + this.current = current; } - @property bool empty() const { return current == pastLast; } + @property bool empty() const { return step == 0; } @property inout(Value) front() inout { assert(!empty); return current; } - void popFront() { assert(!empty); current += step; } + void popFront() + { + assert(!empty); + if (current == last) step = 0; + else current += step; + } @property inout(Value) back() inout { assert(!empty); - return cast(Value) (pastLast - step); + return last; + } + void popBack() + { + assert(!empty); + if (current == last) step = 0; + else last -= step; } - void popBack() { assert(!empty); pastLast -= step; } @property auto save() { return this; } @@ -5333,20 +5340,18 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) { assert(upper >= lower && upper <= this.length); - return cast(inout Result) Result(cast(Value)(current + lower * step), - cast(Value)(pastLast - (length - upper) * step), - step); + return cast(inout Result) Result( + cast(Value)(current + lower * step), + cast(Value)(current + upper * step), + step); } @property size_t length() const { if (step > 0) - { - return cast(size_t)((pastLast - current) / step); - } - else - { - return cast(size_t)((current - pastLast) / -step); - } + return 1 + cast(size_t) ((last - current) / step); + if (step < 0) + return 1 + cast(size_t) ((current - last) / -step); + return 0; } alias opDollar = length; @@ -5751,15 +5756,23 @@ debug @system unittest } } -@safe @nogc nothrow unittest +@nogc nothrow pure @safe +unittest { { ushort start = 0, end = 10, step = 2; - foreach (i; iota(start, end, step)) {} + foreach (i; iota(start, end, step)) + static assert(is(typeof(i) == ushort)); } { - ubyte start = 0, end = 10, step = 2; - foreach (i; iota(start, end, step)) {} + ubyte start = 0, end = 255, step = 128; + uint x; + foreach (i; iota(start, end, step)) + { + static assert(is(typeof(i) == ubyte)); + ++x; + } + assert(x == 2); } }