Thread overview
Why is opIndexAssign replaced by opSlice here?
Oct 17, 2021
Elmar
Oct 17, 2021
Elmar
Oct 18, 2021
Paul Backus
Oct 18, 2021
Paul Backus
Oct 18, 2021
Imperatorn
Oct 18, 2021
Elmar
October 17, 2021

Hello Dear community.

I'd like to overload opIndexAssign for a struct which wraps around a generic array (so that it can't support opIndex due to unknown return type).

Broken down as much as possible this is the code:

import std.stdio : writeln;
import std.range : ElementType;

struct S {
	void opIndexAssign(X, RANGE)(X x, RANGE range)
		if (is(ElementType!RANGE : size_t))
	{
		writeln(__FUNCTION__);
	}

	auto opSlice(size_t start, size_t end) {
		import std.range : iota;
		return iota(start, end);
	}
}

void main()
{
	auto arr = new int[7];

	S s;
	s.opIndexAssign(arr, s.opSlice(1,4));  // works
	s[0..3] = arr[1..4];  // does not work, compiles to `s.opSlice(0,3) = arr[1..4]`
}

I'm clueless about why it wouldn't compile the last statement to s.opIndexAssign(arr[1..4], s.opSlice(0,3)).

Help appreciated :-)

October 17, 2021

Btw, I should have written:

s.opIndexAssign(arr[1..4], s.opSlice(0,3));

But it compiles the same way.

October 18, 2021

On Sunday, 17 October 2021 at 22:52:27 UTC, Elmar wrote:

>

Hello Dear community.

I'd like to overload opIndexAssign for a struct which wraps around a generic array (so that it can't support opIndex due to unknown return type).

Broken down as much as possible this is the code:

import std.stdio : writeln;
import std.range : ElementType;

struct S {
	void opIndexAssign(X, RANGE)(X x, RANGE range)
		if (is(ElementType!RANGE : size_t))
	{
		writeln(__FUNCTION__);
	}

	auto opSlice(size_t start, size_t end) {
		import std.range : iota;
		return iota(start, end);
	}
}

void main()
{
	auto arr = new int[7];

	S s;
	s.opIndexAssign(arr, s.opSlice(1,4));  // works
	s[0..3] = arr[1..4];  // does not work, compiles to `s.opSlice(0,3) = arr[1..4]`
}

I'm clueless about why it wouldn't compile the last statement to s.opIndexAssign(arr[1..4], s.opSlice(0,3)).

Help appreciated :-)

What happens here is, the compiler first tries the D2-style rewrite:

s.opIndexAssign(arr[1..4], s.opSlice!0(0, 3))

However, that rewrite fails to compile, because your opSlice does not take a template argument specifying the dimension along which to slice, as specified in the language spec's section on "Slice Operator Overloading".

Since the above rewrite fails to compile, it falls back to rewriting the expression using D1-style operator overloads:

>

For backward compatibility, a[] and a[i..j] can also be overloaded by implementing opSlice() with no arguments and opSlice(i, j) with two arguments, respectively. This only applies for one-dimensional slicing, and dates from when D did not have full support for multidimensional arrays. This usage of opSlice is discouraged.

...which results in the following rewritten statement:

s.opSlice(0, 3) = arr[1..4];

My guess is that you got into this situation by trying to follow the example in the spec's section on "Slice Assignment Operator Overloading". Unfortunately, that example is incorrect.

Here is a fixed version of your example on run.dlang.io: https://run.dlang.io/is/dtfT5y

October 18, 2021

On Monday, 18 October 2021 at 03:42:35 UTC, Paul Backus wrote:

>

My guess is that you got into this situation by trying to follow the example in the spec's section on "Slice Assignment Operator Overloading". Unfortunately, that example is incorrect.

https://issues.dlang.org/show_bug.cgi?id=22417
https://github.com/dlang/dlang.org/pull/3113

October 18, 2021

On Monday, 18 October 2021 at 04:11:18 UTC, Paul Backus wrote:

>

On Monday, 18 October 2021 at 03:42:35 UTC, Paul Backus wrote:

>

My guess is that you got into this situation by trying to follow the example in the spec's section on "Slice Assignment Operator Overloading". Unfortunately, that example is incorrect.

https://issues.dlang.org/show_bug.cgi?id=22417
https://github.com/dlang/dlang.org/pull/3113

Gold star for fixing broken examples 🌟

October 18, 2021

On Monday, 18 October 2021 at 03:42:35 UTC, Paul Backus wrote:

>

What happens here is, the compiler first tries the D2-style rewrite:

s.opIndexAssign(arr[1..4], s.opSlice!0(0, 3))

However, that rewrite fails to compile, because your opSlice does not take a template argument specifying the dimension along which to slice, as specified in the language spec's section on "Slice Operator Overloading".

Since the above rewrite fails to compile, it falls back to rewriting the expression using D1-style operator overloads:

>

For backward compatibility, a[] and a[i..j] can also be overloaded by implementing opSlice() with no arguments and opSlice(i, j) with two arguments, respectively. This only applies for one-dimensional slicing, and dates from when D did not have full support for multidimensional arrays. This usage of opSlice is discouraged.

...which results in the following rewritten statement:

s.opSlice(0, 3) = arr[1..4];

My guess is that you got into this situation by trying to follow the example in the spec's section on "Slice Assignment Operator Overloading". Unfortunately, that example is incorrect.

Here is a fixed version of your example on run.dlang.io: https://run.dlang.io/is/dtfT5y

Woow! You fixed the problem, it now works for me.

I wish the compiler would have been able to make me understand the problem. It was no help because it only tells the final erroneous consequence, that the return value of opSlice cannot be assigned ("is no lvalue", beginner-unfriendly language). It doesn't even mention opSlice or the fallback (reason) with no warning.