www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - T[] opIndex() Error: .. signal 11

reply Joel <joelcnz gmail.com> writes:
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?

```d
// Adding program - literal functions

import std;

struct List(T) {
     class Node {
         T data;
         Node next;
         this(T data) {
             this.data=data;
         }
     }
     string title;
     size_t length;
     Node head;
     this(string title, T[] data...) {
         this.title=title;
         length=data.length;
         if (length) {
             head=new Node(data[0]);
             auto cur=head;
             data[1..$].each!((d) {
                 cur.next=new Node(d);
                 cur=cur.next;
             });
         }
     }
     bool empty() { return head is null; }
     auto ref front() { return head.data; }
     void popFront() { head=head.next; }
     T[] opIndex() {
         return this.array;
     }
     auto opDollar() {
         return length;
     }
}

void main(string[] args) {
     args.popFront;
     List!int ints;
     if (args.length) {
         ints=List!int(args[0], args[1..$].to!(int[]));
     } else{
         ints=List!int("Car, and Date numbers", 1979,9,3,4,5);
     }
     stdout.write(ints.title, ": ");
     ints.each!((i, d) {
         stdout.write(d, i+1<ints.length ? "+" : "=");
     });
     writeln(ints.sum);
}
```
Oct 03 2023
next sibling parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 3 October 2023 at 15:12:34 UTC, 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?

 ```d
 // Adding program - literal functions

 import std;

 struct List(T) {
     class Node {
         T data;
         Node next;
         this(T data) {
             this.data=data;
         }
     }
     string title;
     size_t length;
     Node head;
     this(string title, T[] data...) {
         this.title=title;
         length=data.length;
         if (length) {
             head=new Node(data[0]);
             auto cur=head;
             data[1..$].each!((d) {
                 cur.next=new Node(d);
                 cur=cur.next;
             });
         }
     }
     bool empty() { return head is null; }
     auto ref front() { return head.data; }
     void popFront() { head=head.next; }
     T[] opIndex() {
         return this.array;
     }
     auto opDollar() {
         return length;
     }
 }

 void main(string[] args) {
     args.popFront;
     List!int ints;
     if (args.length) {
         ints=List!int(args[0], args[1..$].to!(int[]));
     } else{
         ints=List!int("Car, and Date numbers", 1979,9,3,4,5);
     }
     stdout.write(ints.title, ": ");
     ints.each!((i, d) {
         stdout.write(d, i+1<ints.length ? "+" : "=");
     });
     writeln(ints.sum);
 }
 ```
Remove: It didn't segfault for me, but it still didn't print what's in the ``ints``, but if you remove the following code: ``` T[] opIndex() { return this.array; } auto opDollar() { return length; } ``` ..then it'll work Sounds like a bug.. i don't use these D features, maybe someone else have more insights? Either way, what a terrible experience
Oct 03 2023
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Oct 03 2023
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 3 October 2023 at 17:05:46 UTC, Steven Schveighoffer 
wrote:
 ```d
 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[]`.
Naturally, this lowering is completely absent from [the language spec's section on `foreach`.][1] According to the spec, the only ways to iterate over a `struct` type are `opApply` and the input range interface. I think it would probably be less confusing to have both `opApply` and `empty`/`front`/`popFront` take precedence over this lowering, but I have no idea how much existing code would be broken by such a change. At the very least, though, this needs to be documented. [1]: https://dlang.org/spec/statement.html#foreach-statement
Oct 03 2023
parent reply ag0aep6g <anonymous example.com> writes:
On 03.10.23 20:26, Paul Backus wrote:
 Naturally, this lowering is completely absent from [the language spec's 
 section on `foreach`.][1] According to the spec, the only ways to 
 iterate over a `struct` type are `opApply` and the input range interface.
 
 I think it would probably be less confusing to have both `opApply` and 
 `empty`/`front`/`popFront` take precedence over this lowering, but I 
 have no idea how much existing code would be broken by such a change. At 
 the very least, though, this needs to be documented.
 
 [1]: https://dlang.org/spec/statement.html#foreach-statement
For some further reading, there's an open issue about the unexpected slicing: https://issues.dlang.org/show_bug.cgi?id=14619
Oct 04 2023
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/5/23 1:49 AM, ag0aep6g wrote:

 For some further reading, there's an open issue about the unexpected 
 slicing: https://issues.dlang.org/show_bug.cgi?id=14619
Thank you I had forgotten about that issue! -Steve
Oct 05 2023