Bug 13958 – RangeError with impure map

Status
RESOLVED
Resolution
INVALID
Severity
critical
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2015-01-09T15:37:00Z
Last change time
2017-02-07T18:55:49Z
Assigned to
nobody
Creator
john.loughran.colvin

Comments

Comment #0 by john.loughran.colvin — 2015-01-09T15:37:19Z
//incBug.d import std.stdio, std.algorithm, std.array; static a = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]; void main() { size_t idx = 0; writeln(a.map!(s => s ~ a[idx++]).joiner(", ")); } $ rdmd incBud.g [email protected](9): Range violation ---------------- 5 incBug 0x00000001012a0ca1 D6incBug7__arrayZ + 41 6 incBug 0x00000001012a1053 nothrow @safe immutable(char)[] incBug.main().__lambda1!(immutable(char)[]).__lambda1(immutable(char)[]) + 67 7 incBug 0x00000001012a12b4 nothrow @property @safe immutable(char)[] std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult.front() + 108 8 incBug 0x00000001012a213b nothrow @safe void std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result.popFront() + 211 9 incBug 0x00000001012a32be void std.stdio.File.LockingTextWriter.put!(std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result).put(std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result) + 134 10 incBug 0x00000001012a322d void std.range.doPut!(std.stdio.File.LockingTextWriter, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result).doPut(ref std.stdio.File.LockingTextWriter, ref std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result) + 37 11 incBug 0x00000001012a3204 void std.range.put!(std.stdio.File.LockingTextWriter, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result).put(ref std.stdio.File.LockingTextWriter, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result) + 20 12 incBug 0x00000001012a2cea void std.format.formatRange!(std.stdio.File.LockingTextWriter, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, char).formatRange(ref std.stdio.File.LockingTextWriter, ref std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, ref std.format.FormatSpec!(char).FormatSpec) + 298 13 incBug 0x00000001012a2b87 void std.format.formatValue!(std.stdio.File.LockingTextWriter, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, char).formatValue(std.stdio.File.LockingTextWriter, ref std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, ref std.format.FormatSpec!(char).FormatSpec) + 47 14 incBug 0x00000001012a2b30 void std.format.formatGeneric!(std.stdio.File.LockingTextWriter, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, char).formatGeneric(std.stdio.File.LockingTextWriter, const(void)*, ref std.format.FormatSpec!(char).FormatSpec) + 64 15 incBug 0x00000001012a2a31 uint std.format.formattedWrite!(std.stdio.File.LockingTextWriter, char, std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result).formattedWrite(std.stdio.File.LockingTextWriter, const(char[]), std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result) + 1209 16 incBug 0x00000001012a2535 void std.stdio.File.write!(std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, char).write(std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result, char) + 189 17 incBug 0x00000001012a246b void std.stdio.writeln!(std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result).writeln(std.algorithm.joiner!(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).joiner(std.algorithm.__T9MapResultS246incBug4mainFZ9__lambda1TAAyaZ.MapResult, immutable(char)[]).Result) + 43 18 incBug 0x00000001012a0c6a _Dmain + 170 19 incBug 0x00000001012b88d4 D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv + 40 20 incBug 0x00000001012b8819 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) + 45 21 incBug 0x00000001012b8879 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() + 45 22 incBug 0x00000001012b8819 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) + 45 23 incBug 0x00000001012b8795 _d_run_main + 433 24 incBug 0x00000001012a0d1c main + 20 25 libdyld.dylib 0x00007fff8fd5d5c9 start + 1 26 ??? 0x0000000000000001 0x0 + 1 aa, $ I thought perhaps it was because of referencing `a` twice, but it does the same with 2 different arrays.
Comment #1 by bearophile_hugs — 2015-01-09T15:46:04Z
The short answer: do not use impure functions in higher order functions like map/filter, unless you know what you are doing (and you usually don't). So it's better to find a pure way to solve your problem, like: void main() { import std.stdio, std.algorithm, std.array, std.range; static a = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]; zip(a, a).map!(p => p[0] ~ p[1]).joiner(", ").writeln; }
Comment #2 by peter.alexander.au — 2015-01-11T12:23:01Z
This isn't a bug. You cannot make assumptions about how many times joiner will call .front. Since map is a forward range, you cannot assume that joiner will only iterate once. Like bearophile, I would highly recommend using pure functions with map.
Comment #3 by jack — 2017-02-07T18:55:49Z
Using side effects in range code is a recipe for disaster. From the std.range.isInputRange docs, >The following are rules of input ranges are assumed to hold true in all Phobos code. These rules are not checkable at compile-time, so not conforming to these rules when writing ranges or range based code will result in undefined behavior. >... >r.front evaluated multiple times, without calling r.popFront, or otherwise mutating the range object or the underlying data, yields the same result for every evaluation. This code breaks that assumption, as map does not cache the results of the lambda. However this works, import std.stdio, std.algorithm, std.array; static a = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]; void main() { size_t idx = 0; writeln(a.map!(s => s ~ a[idx + 1 == a.length ? idx : ++idx]).cache.joiner(", ")); } Closing as invalid.