T[] opIndex() Error: .. signal 11
Steven Schveighoffer
schveiguy at gmail.com
Tue Oct 3 17:05:46 UTC 2023
On 10/3/23 11:12 AM, Joel wrote:
> The following program crashes, but doesn’t if I change (see title) T[]
> to auto. The program doesn’t even use that method/function. What’s the
> story?
It's a stack overflow.
when doing foreach on your type, the compiler *always* uses a slice
first if it compiles and is a valid range.
So `foreach(x; ints)` really translates to `foreach(x; ints[])`.
Normally not a problem. But your `opIndex()` is calling `this.array`.
What does `this.array` do? a foreach on your type. Which calls
`opIndex`, which calls `array`, which calls `opIndex`, etc.
When you make it auto, well, then inside the `array` function, it won't
use the `opIndex` (because clearly, the type hasn't been determined).
And so it goes with the range functions without first doing a slice.
But then outside the type, now that `opIndex` type has been inferred, it
can now use `foreach(x; ints[])`, and that goes back to the regular
mechanism.
A minimized case is here:
```d
struct S
{
int front() => 1;
void popFront() {}
bool empty() => true;
auto opIndex() {
foreach(x; this) {}
return int[].init;
}
}
void main()
{
S s;
foreach(x; s) {}
}
```
If you run this on run.dlang.io, and click the "AST" button, you will
get this for the type and the main function:
```d
import object;
struct S
{
int front()
{
return 1;
}
void popFront()
{
}
bool empty()
{
return true;
}
auto @system int[] opIndex()
{
{
S __r2 = this;
for (; !__r2.empty(); __r2.popFront())
{
int x = __r2.front();
}
}
return null;
}
}
void main()
{
S s = 0;
{
scope int[] __r3 = s.opIndex()[];
ulong __key4 = 0LU;
for (; __key4 < __r3.length; __key4 += 1LU)
{
int x = __r3[__key4];
}
}
return 0;
}
```
Note the difference in how the foreach code is lowered. Inside
`opIndex`, it's lowered to the range functions. Outside, it uses the
slice operator to switch to iterating a `scope int[]`.
If you now switch the `auto` to `int[]`, it's a segfault, because now
the `opIndex` has a concrete return type, and it *can* use the
`opIndex`, inside `opIndex`.
I really think the implicit slice should be revisited. It shouldn't
happen in this case.
-Steve
More information about the Digitalmars-d-learn
mailing list