Thread overview
Understanding opAssign and 'alias this'
Oct 13, 2013
Maurice
Oct 13, 2013
Kozzi
Oct 14, 2013
Maurice
Oct 14, 2013
John Colvin
Oct 14, 2013
Maxim Fomin
Oct 14, 2013
Maurice
Oct 14, 2013
Maurice
Oct 14, 2013
Maxim Fomin
October 13, 2013
Hello,

Can anybody explain my what is happening here?

enum xxx = true;

struct A {
	static if (xxx) ref A opAssign(ref const A a) { return this; }
	ref A opAssign(int v) { return this; }
}

struct B {
	A a;
	alias a this;
}

void main() {
	A a;
	B b;
	a = b; // [1]
	b = a; // [2]
}

When xxx is false:
[1] Gives an error
[2] Compiles fine

When xxx is true:
[1] Compiles fine
[2] Gives an error

What exactly is happening here? When xxx is false, what does 'b = a' do that is wrong when xxx is true?

I can't find any clear documentation about opAssign and its 'default' implementation.

Thanks!

-Maurice-
October 13, 2013
On Sunday, 13 October 2013 at 21:37:31 UTC, Maurice wrote:
> Hello,
>
> Can anybody explain my what is happening here?
>
> enum xxx = true;
>
> struct A {
> 	static if (xxx) ref A opAssign(ref const A a) { return this; }
> 	ref A opAssign(int v) { return this; }
> }
>
> struct B {
> 	A a;
> 	alias a this;
> }
>
> void main() {
> 	A a;
> 	B b;
> 	a = b; // [1]
> 	b = a; // [2]
> }
>
> When xxx is false:
> [1] Gives an error
> [2] Compiles fine
>
> When xxx is true:
> [1] Compiles fine
> [2] Gives an error
>
> What exactly is happening here? When xxx is false, what does 'b = a' do that is wrong when xxx is true?
>
> I can't find any clear documentation about opAssign and its 'default' implementation.
>
> Thanks!
>
> -Maurice-

It is a bug from my POV
October 14, 2013
On Sunday, 13 October 2013 at 22:47:12 UTC, Kozzi wrote:
>
> It is a bug from my POV

It sure seems like it. But what would be the correct behaviour? Should [1] and [2] both work in both cases? What are the rules around the default opAssign? Does the default one disappear by adding a opAssign(int)?
October 14, 2013
On Sunday, 13 October 2013 at 21:37:31 UTC, Maurice wrote:
> Hello,
>
> Can anybody explain my what is happening here?
>
> enum xxx = true;
>
> struct A {
> 	static if (xxx) ref A opAssign(ref const A a) { return this; }
> 	ref A opAssign(int v) { return this; }
> }
>
> struct B {
> 	A a;
> 	alias a this;
> }
>
> void main() {
> 	A a;
> 	B b;
> 	a = b; // [1]
> 	b = a; // [2]
> }
>
> When xxx is false:
> [1] Gives an error
> [2] Compiles fine
>
> When xxx is true:
> [1] Compiles fine
> [2] Gives an error
>
> What exactly is happening here? When xxx is false, what does 'b = a' do that is wrong when xxx is true?
>
> I can't find any clear documentation about opAssign and its 'default' implementation.
>
> Thanks!
>
> -Maurice-

Everything is working fine except for the error on [2] when xxx == true, which I think is a bug.

minimised test:

struct A
{
        void opAssign(A a) {}
}

struct B {
        A a;
        alias a this;
}

void main() {
        A a;
        B b;
        b = a;
}

Error: function assign.B.opAssign (B p) is not callable using argument types (A)
October 14, 2013
On Monday, 14 October 2013 at 09:17:12 UTC, John Colvin wrote:
> Everything is working fine except for the error on [2] when xxx == true, which I think is a bug.
>
> minimised test:
>
> struct A
> {
>         void opAssign(A a) {}
> }
>
> struct B {
>         A a;
>         alias a this;
> }
>
> void main() {
>         A a;
>         B b;
>         b = a;
> }
>
> Error: function assign.B.opAssign (B p) is not callable using argument types (A)

This does not work (and need not) because compiler generates default function B.opAssign(B) which is really not callable using argument types (A).
October 14, 2013
On Monday, 14 October 2013 at 09:32:15 UTC, Maxim Fomin wrote:
> On Monday, 14 October 2013 at 09:17:12 UTC, John Colvin wrote:
>> Everything is working fine except for the error on [2] when xxx == true, which I think is a bug.
>>
>> minimised test:
>>
>> struct A
>> {
>>        void opAssign(A a) {}
>> }
>>
>> struct B {
>>        A a;
>>        alias a this;
>> }
>>
>> void main() {
>>        A a;
>>        B b;
>>        b = a;
>> }
>>
>> Error: function assign.B.opAssign (B p) is not callable using argument types (A)
>
> This does not work (and need not) because compiler generates default function B.opAssign(B) which is really not callable using argument types (A).

Then why does it work when replacing "opAssign(A a)" with "opAssign(int)"?

struct A {
        void opAssign(int) {}
}

struct B {
        A a;
        alias a this;
}

void main() {
        A a;
        B b;
        b = a; // This now compiles fine...
}

October 14, 2013
On Monday, 14 October 2013 at 10:45:26 UTC, Maurice wrote:
> On Monday, 14 October 2013 at 09:32:15 UTC, Maxim Fomin wrote:
>> On Monday, 14 October 2013 at 09:17:12 UTC, John Colvin wrote:
>>> Everything is working fine except for the error on [2] when xxx == true, which I think is a bug.
>>>
>>> minimised test:
>>>
>>> struct A
>>> {
>>>       void opAssign(A a) {}
>>> }
>>>
>>> struct B {
>>>       A a;
>>>       alias a this;
>>> }
>>>
>>> void main() {
>>>       A a;
>>>       B b;
>>>       b = a;
>>> }
>>>
>>> Error: function assign.B.opAssign (B p) is not callable using argument types (A)
>>
>> This does not work (and need not) because compiler generates default function B.opAssign(B) which is really not callable using argument types (A).
>
> Then why does it work when replacing "opAssign(A a)" with "opAssign(int)"?
>
> struct A {
>         void opAssign(int) {}
> }
>
> struct B {
>         A a;
>         alias a this;
> }
>
> void main() {
>         A a;
>         B b;
>         b = a; // This now compiles fine...
> }

Or when just not defining any opAssign: struct A {}, that also works. I don't get how adding a opAssign(A) disables 'b = a'.
October 14, 2013
On Monday, 14 October 2013 at 10:45:26 UTC, Maurice wrote:
> On Monday, 14 October 2013 at 09:32:15 UTC, Maxim Fomin wrote:
>> On Monday, 14 October 2013 at 09:17:12 UTC, John Colvin wrote:
>>> Everything is working fine except for the error on [2] when xxx == true, which I think is a bug.
>>>
>>> minimised test:
>>>
>>> struct A
>>> {
>>>       void opAssign(A a) {}
>>> }
>>>
>>> struct B {
>>>       A a;
>>>       alias a this;
>>> }
>>>
>>> void main() {
>>>       A a;
>>>       B b;
>>>       b = a;
>>> }
>>>
>>> Error: function assign.B.opAssign (B p) is not callable using argument types (A)
>>
>> This does not work (and need not) because compiler generates default function B.opAssign(B) which is really not callable using argument types (A).
>
> Then why does it work when replacing "opAssign(A a)" with "opAssign(int)"?
>
> struct A {
>         void opAssign(int) {}
> }
>
> struct B {
>         A a;
>         alias a this;
> }
>
> void main() {
>         A a;
>         B b;
>         b = a; // This now compiles fine...
> }

OK, it seems to be what actually causes the problem is implicit conversion from B to A. Situation depends on presence of A.opAssign(A ) because when compiler tries to resolve function call, it is confused between calling B.opAssign(B) and casting through alias this B to A and calling A.opAssign(A). When it realizes that there is no best match it throws error. Since argument B is casted to A, the error message looks so weird.