October 10, 2014
On 10/10/2014 10:09 AM, IgorStepanov wrote:
> I've created DIP for my pull request.
> DIP: http://wiki.dlang.org/DIP66
> PR: https://github.com/D-Programming-Language/dmd/pull/3998
>
> Please, comment it.

Thanks, Igor!
October 10, 2014
On Friday, 10 October 2014 at 20:47:45 UTC, Steven Schveighoffer wrote:
> On 10/10/14 1:09 PM, IgorStepanov wrote:
>> I've created DIP for my pull request.
>> DIP: http://wiki.dlang.org/DIP66
>> PR: https://github.com/D-Programming-Language/dmd/pull/3998
>>
>> Please, comment it.
>
> This part:
>
> void test()
>     {
>         C c;
>         int i = c; //Error: c.a.i vs c.b.i
>     }
>
>     static assert(is(C : int)); //Ok, because C is subtype of int anyway.
>
> I think might be wrong. There is a lot of code out there that says, e.g.:
>
> void foo(T)(T t) if(is(T : U))
> {
>   U u = t;
>   ...
> }
>
> Which will now create an error in the wrong place. IMO, the 'is' test should also fail.
>
> -Steve

I thought exactly about this using case.

See:
You have a struct like this in first place:
    struct A
    {
        int i;
        alias i this;
    }

    struct C
    {
        A a;
        string s;
        alias a this;
        alias s this;
    }

And you have a template function in second place:
void foo(T)(T t) if(is(T : int))
{
...
}

void foo(T)(T t) if(is(T : string))
{
...
}


And you have the code it third place:
C c;
foo(c); //Error: what do you mean: foo!(T : string) or foo!(T : int)

Now, someone (A developer) changed the A definition:

    struct A
    {
        int i;
        alias i this;
    }

    struct B
    {
        int i;
        alias i this;
    }

    struct C
    {
        A a;
        B b;
        string s;
        alias a this;
        alias b this;
        alias s this;
    }

And now, you code mystically start to works.
Attention: Infusing in one place conflict resolves conflict in another place.
It is danger, I think.
October 10, 2014
> Which will now create an error in the wrong place. IMO, the 'is' test should also fail.
>
> -Steve

In this case, you will see the real error and will able to fix if. For example call foo(c.a); Otherwice, you possible error will be fixed without taking into account your opinions.
October 10, 2014
On Friday, 10 October 2014 at 20:29:43 UTC, Brian Schott wrote:
> On Friday, 10 October 2014 at 17:09:08 UTC, IgorStepanov wrote:
>> I've created DIP for my pull request.
>> DIP: http://wiki.dlang.org/DIP66
>> PR: https://github.com/D-Programming-Language/dmd/pull/3998
>>
>> Please, comment it.
>
> There is an error in the wiki formatting in the second code block. (`{| class="wikitable"` should not be there)
>
> I don't see any problems with the actual content of the DIP.

Fixed. This is starnge implicit copy-paste of my text editor =/
October 10, 2014
On 10/10/2014 10:09 AM, IgorStepanov wrote:
> Please, comment it.

> static assert(is(C : int)); //Ok, because C is subtype of int anyway.

Comment should say that C is implicitly convertible to int

    struct Test1
    {
        int a;
        int b;
        alias a this;
        alias b this; //Error: alias b this conflicts with alias a this;
    }

DIP says this is "obviously incorrect", but what rule is being applied? I suspect the rule here is if typeof(a)==typeof(b), then it is rejected.

What if typeof(a) is implicitly convertible to typeof(b), and vice-versa?

Is alias this resolved before base class search, after base class search, or is it an error if both searches are successful?

If 'a' and 'b' both contain overloads for function foo, then it should behave like imports do (which is a bit complex).


Essentially, the rules for multiple alias this should be the same as for multiple imports and multiple mixin templates. These rules work, and the consistency will be expected.
October 10, 2014
On 10/10/14 5:15 PM, IgorStepanov wrote:
> On Friday, 10 October 2014 at 20:47:45 UTC, Steven Schveighoffer wrote:
>> On 10/10/14 1:09 PM, IgorStepanov wrote:
>>> I've created DIP for my pull request.
>>> DIP: http://wiki.dlang.org/DIP66
>>> PR: https://github.com/D-Programming-Language/dmd/pull/3998
>>>
>>> Please, comment it.
>>
>> This part:
>>
>> void test()
>>     {
>>         C c;
>>         int i = c; //Error: c.a.i vs c.b.i
>>     }
>>
>>     static assert(is(C : int)); //Ok, because C is subtype of int anyway.
>>
>> I think might be wrong. There is a lot of code out there that says, e.g.:
>>
>> void foo(T)(T t) if(is(T : U))
>> {
>>   U u = t;
>>   ...
>> }
>>
>> Which will now create an error in the wrong place. IMO, the 'is' test
>> should also fail.
>>
>> -Steve
>
> I thought exactly about this using case.
>
> See:
> You have a struct like this in first place:
>      struct A
>      {
>          int i;
>          alias i this;
>      }
>
>      struct C
>      {
>          A a;
>          string s;
>          alias a this;
>          alias s this;
>      }
>
> And you have a template function in second place:
> void foo(T)(T t) if(is(T : int))
> {
> ...
> }
>
> void foo(T)(T t) if(is(T : string))
> {
> ...
> }
>
>
> And you have the code it third place:
> C c;
> foo(c); //Error: what do you mean: foo!(T : string) or foo!(T : int)

I agree with all this.

> Now, someone (A developer) changed the A definition:
>
>      struct A
>      {
>          int i;
>          alias i this;
>      }
>
>      struct B
>      {
>          int i;
>          alias i this;
>      }
>
>      struct C
>      {
>          A a;
>          B b;
>          string s;
>          alias a this;
>          alias b this;
>          alias s this;
>      }
>
> And now, you code mystically start to works.

Why? It's just as confused as before, no?

The way the DIP reads, the call to foo(c) compiles, but the instantiation fails. This can cause subtle issues when you want to select an instantiation based on what it casts to.

An example:

foo(T)(T t) if(is(T : int))
{
   someFuncThatTakesInt(t);
}

foo(T)(T t) if(!is(T : int) && is(T.shadow : int))
{
   someFuncThatTakesInt(t.shadow);
}

struct A
{
   int i;
   alias i this;
}

struct B
{
   int i;
   alias i this;
}

struct C
{
   A a;
   B shadow;
   alias a this;
   alias shadow this;
}

C c;
foo(c); // should compile, but I think your DIP makes it fail due to ambiguity

-Steve
October 10, 2014
On Friday, 10 October 2014 at 20:44:11 UTC, Marc Schütz wrote:
> On Friday, 10 October 2014 at 17:09:08 UTC, IgorStepanov wrote:
>> I've created DIP for my pull request.
>> DIP: http://wiki.dlang.org/DIP66
>> PR: https://github.com/D-Programming-Language/dmd/pull/3998
>>
>> Please, comment it.
>
> I understand that as a first step it was suggested to implement the strictest behaviour regarding conflicts, namely to disallow them entirely.
>
> However, I think more permissive strategies are useful, like in this example:
> https://github.com/D-Programming-Language/dmd/pull/3998#issuecomment-58570742
>
> Conflict resolution can work like overload resolution, with different levels of matching and partial ordering.

There isn't hard to change resolving strategy. This will need to change one function definition.
Thus we able to implement strictest now and after some time of new feature using, relax the behaviour. This will not require significant efforts.
October 10, 2014
On 10/10/2014 07:09 PM, IgorStepanov wrote:
> I've created DIP for my pull request.
> DIP: http://wiki.dlang.org/DIP66
> PR: https://github.com/D-Programming-Language/dmd/pull/3998
>
> Please, comment it.

- "C c;
int i = c; //Error: c.a.i vs c.b.i

static assert(is(C : int)); //Ok, because C is subtype of int anyway."

So now we can have 'subtypes' whose instances cannot be stored in variables of the 'base type'?

Such behaviour is inconsistent with both the reference implementation and the documentation (and the two happen to be mutually inconsistent on how 'is(:)' should behave as well. :o) )


- "The following pseudo-code illustrates this: [...] Finally, if resultSet contains only one candidate, the compiler will accept it."

This process might very well never terminate but it could terminate in more cases if it did something better than the naive brute-force search. I.e. either report cycles or don't keep exploring around cycles, but just looping indefinitely on cycles like the following is IMO not a good course of action:

struct S{
    alias get this;
    T get(){ return T.init; }
}
struct T{
    alias get this;
    S get(){ return S.init; }
    int x;
    alias x this;
}

void main(){
    S s;
    int x=s;
}

Furthermore, the following code compiles now, but doesn't under the approach described in the DIP. Is this an actual regression your pull introduces or is there a bug in the pseudocode?:

class A{
    alias x this;
    int x;
}

class B: A{
    alias y this;
    int y;
}

void main(){
    int x = new B();
}

The same issue also needs to be considered if A and B are structs instead and B has an additional alias this to an A (the solution might also be part of a fix for the cycle issue).

- "If resultSet contains more then one candidates, the compiler raises an error."

Why? The language already has a way to deal with cross-scope overload resolution. (Also, what makes candidates different?)


October 10, 2014
On 10/10/2014 11:25 PM, Walter Bright wrote:
>
> Essentially, the rules for multiple alias this should be the same as for
> multiple imports and multiple mixin templates. These rules work, and the
> consistency will be expected.

Agreed. Do you suggest to overload alias this against imports and mixin templates?
October 10, 2014
On Friday, 10 October 2014 at 21:25:17 UTC, Walter Bright wrote:
> On 10/10/2014 10:09 AM, IgorStepanov wrote:
>> Please, comment it.
>
> > static assert(is(C : int)); //Ok, because C is subtype of int
> anyway.
>
> Comment should say that C is implicitly convertible to int

Not really.
int i = C(); //conflict: c.a.i vs c.b.i (thus, C is not implicitly convertible to int)

>     struct Test1
>     {
>         int a;
>         int b;
>         alias a this;
>         alias b this; //Error: alias b this conflicts with alias a this;
>     }
>
> DIP says this is "obviously incorrect", but what rule is being applied? I suspect the rule here is if typeof(a)==typeof(b), then it is rejected.
>
> What if typeof(a) is implicitly convertible to typeof(b), and vice-versa?

In current state, if "typeof(a) is implicitly convertible to typeof(b), and vice-versa", compiler will accept this alias this declaration.
However error may be raised at alias this resolving stage.

> Is alias this resolved before base class search, after base class search, or is it an error if both searches are successful?

In existing alias this implementation alias this resolved after base class search.  This DIP inherits it, thus now I suggest to check base classes before alias this. Is it acceptable? Should I add this into DIP?

> If 'a' and 'b' both contain overloads for function foo, then it should behave like imports do (which is a bit complex).

Hmm. Now it works as I wrote it DIP pseudo-code:
struct A
{
    void foo(string);
    void foo(int);
}

struct B
{
    void foo(double);
}

struct C
{
    A a;
    B b;
    alias a this;
    alias b this;
}

C.foo("test"); //found only one acceptable foo: C.a.foo(string)
C.foo(5);      //1. Check a: found C.a.foo(int);
               //2. Check b: found C.b.foo(double);
               //3. Raise error: C.a.foo(int) vs C.b.foo(double) conflict
C.foo(5.0);    //found only one acceptable foo: C.b.foo(double)

Is it Ok?

> Essentially, the rules for multiple alias this should be the same as for multiple imports and multiple mixin templates. These rules work, and the consistency will be expected.

Where can I read about multiple mixin templates?