October 10, 2014
On Friday, 10 October 2014 at 21:26:49 UTC, Steven Schveighoffer wrote:
> 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

You can write foo(c.shadow); This isn't hard.
Ok, I understood you, let's listen to what others say
October 10, 2014
On 10/11/2014 12:07 AM, IgorStepanov wrote:
> On Friday, 10 October 2014 at 21:25:17 UTC, Walter Bright wrote:
>>
>> 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:

The pseudo-code doesn't actually specify that the arguments the identifier is being called with are considered at all.

> 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?
> ...

That is the right behaviour. What happens in this case:

struct A{
    void foo(int);
    void foo(double);
}

struct B
    void foo(string);
}

... // (struct C and calls as yours)



Is it correct that the DIP makes imports at aggregate scope shadow alias this lookups?
October 10, 2014
On Friday, 10 October 2014 at 22:05:18 UTC, Timon Gehr wrote:
> 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'?

C++ allowed subtypes, which can not be casted to the base type (inheritance of two identical, non-virtual base classes).
Ok, I've wrote my position, understood your and wait the decision of the arbitrator:)

> 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;
> }

This case described in DIP below.
Recursion tree will be like:
s.get
   s.get.get ->return, because T is already visited
s.x -> win

> 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."

struct A
{
   short s;
   alias s this;
}

struct B
{
   int i;
   alias i this;
}

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

long l = C(); //What do you suggest?
October 10, 2014
On Friday, 10 October 2014 at 22:18:36 UTC, Timon Gehr wrote:
> On 10/11/2014 12:07 AM, IgorStepanov wrote:
>> On Friday, 10 October 2014 at 21:25:17 UTC, Walter Bright wrote:
>>>
>>> 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:
>
> The pseudo-code doesn't actually specify that the arguments the identifier is being called with are considered at all.
>
>> 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?
>> ...
>
> That is the right behaviour. What happens in this case:
>
> struct A{
>     void foo(int);
>     void foo(double);
> }
>
> struct B
>     void foo(string);
> }
>
> ... // (struct C and calls as yours)

C.foo("test"); //Ok, C.b.foo(string);
C.foo(5);      //Ok, C.a.foo(int);
C.foo(5.0);    //Ok, C.a.foo(double);

Compiler simply tries to forward c.foo(ARG) -> c.a.foo(ARG) and c.b.foo(ARG). If only one is correct, compiler will accept it. If both is correct, compiler will raise an error.
October 10, 2014
On 10/10/14 19:09, IgorStepanov via Digitalmars-d 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 may be beyond the scope of the DIP, but is there any intention (or any need?) to set rules for how alias this should behave in the case of different protection levels for the base type and the alias?

I ask because there is a known issue related to this:
https://issues.dlang.org/show_bug.cgi?id=10996

... but also because any rules set for this, might in principle affect the desirable priority rules for multiple alias this too.
October 10, 2014
On 10/10/2014 3:06 PM, Timon Gehr wrote:
> 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?

I hadn't thought of that (thanks for bringing it up). My first thought is no. Alias this gets searched after those do, because it comes into play only when the symbol isn't resolved in the scope.
October 10, 2014
On 10/11/2014 12:29 AM, Walter Bright wrote:
> On 10/10/2014 3:06 PM, Timon Gehr wrote:
>> 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?
>
> I hadn't thought of that (thanks for bringing it up). My first thought
> is no. Alias this gets searched after those do, because it comes into
> play only when the symbol isn't resolved in the scope.

This allows for symbol hijacking (this is also the current behaviour):

// ---

module m;
import std.stdio;
// void foo(int x){ writeln("hi from m"); } // uncomment to hijack

// ---

module main;
import std.stdio;

struct T{
    import m;
    alias s this;
    S s;
}


struct S{
    void foo(int x){ writeln("hi from S"); }
}

void main(){
    T t;
    t.foo(1);
}
October 10, 2014
On 10/10/2014 3:20 PM, IgorStepanov wrote:
>> 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."
>
> struct A
> {
>     short s;
>     alias s this;
> }
>
> struct B
> {
>     int i;
>     alias i this;
> }
>
> struct C
> {
>     A a;
>     B b;
>     alias a this;
>     alias b this;
> }
>
> long l = C(); //What do you suggest?

The rule would be if:

   long l = C.a();
   long l = C.b();

both compile, then:

   long l = C();

must be an error, even if one of C.a() or C.b() might be a "better" match. This is how things work for template mixins and imports.
October 10, 2014
On 10/10/2014 3:46 PM, Timon Gehr wrote:
> On 10/11/2014 12:29 AM, Walter Bright wrote:
>> On 10/10/2014 3:06 PM, Timon Gehr wrote:
>>> 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?
>>
>> I hadn't thought of that (thanks for bringing it up). My first thought
>> is no. Alias this gets searched after those do, because it comes into
>> play only when the symbol isn't resolved in the scope.
>
> This allows for symbol hijacking (this is also the current behaviour):
>
> // ---
>
> module m;
> import std.stdio;
> // void foo(int x){ writeln("hi from m"); } // uncomment to hijack
>
> // ---
>
> module main;
> import std.stdio;
>
> struct T{
>      import m;
>      alias s this;
>      S s;
> }
>
>
> struct S{
>      void foo(int x){ writeln("hi from S"); }
> }
>
> void main(){
>      T t;
>      t.foo(1);
> }

Hmm. Good point. The alias this should be done before imports.
October 10, 2014
On 10/10/2014 3:27 PM, Joseph Rushton Wakeling via Digitalmars-d wrote:
> On 10/10/14 19:09, IgorStepanov via Digitalmars-d 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 may be beyond the scope of the DIP, but is there any intention (or any
> need?) to set rules for how alias this should behave in the case of different
> protection levels for the base type and the alias?

I like the C++ rule that says that access control is not considered for name lookup. I know it makes for some annoying results, but the simplicity of the rule makes it much more understandable.

>
> I ask because there is a known issue related to this:
> https://issues.dlang.org/show_bug.cgi?id=10996
>
> ... but also because any rules set for this, might in principle affect the
> desirable priority rules for multiple alias this too.