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

Reply via email to