Discussion:
DIP66 - Multiple alias this
IgorStepanov via Digitalmars-d
2014-10-10 17:09:07 UTC
Permalink
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.
Steven Schveighoffer via Digitalmars-d
2014-10-10 17:31:23 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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.
Hm... not sure you need a DIP.

From page 231 of TDPL: "A class could introduce any number of alias
this declarations, thus subtyping any number of types."

-Steve
IgorStepanov via Digitalmars-d
2014-10-10 17:48:51 UTC
Permalink
On Friday, 10 October 2014 at 17:31:23 UTC, Steven Schveighoffer
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
Hm... not sure you need a DIP.
From page 231 of TDPL: "A class could introduce any number of
alias this declarations, thus subtyping any number of types."
-Steve
TDPL tells that multiple alias this should be allowed, but tell
nothing about conflict resolving. General idea of this DIP is
sistematize rules for alias this. Please comment this part. Maybe
I've forgot some cases, maybe introduce dangerous semantic rule.
Brad Anderson via Digitalmars-d
2014-10-10 17:57:14 UTC
Permalink
On Friday, 10 October 2014 at 17:31:23 UTC, Steven Schveighoffer
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
Hm... not sure you need a DIP.
From page 231 of TDPL: "A class could introduce any number of
alias this declarations, thus subtyping any number of types."
-Steve
Igor was asked to write a DIP for it by Walter and Andrei. Here's
the context:
https://github.com/D-Programming-Language/dmd/pull/3998#issuecomment-58286631
Andrei Alexandrescu via Digitalmars-d
2014-10-10 18:17:52 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
Hm... not sure you need a DIP.
From page 231 of TDPL: "A class could introduce any number of alias
this declarations, thus subtyping any number of types."
TDPL is not reference. The DIP should cover various corner cases. -- Andrei
Steven Schveighoffer via Digitalmars-d
2014-10-10 19:04:09 UTC
Permalink
Post by Andrei Alexandrescu via Digitalmars-d
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
Hm... not sure you need a DIP.
From page 231 of TDPL: "A class could introduce any number of alias
this declarations, thus subtyping any number of types."
TDPL is not reference. The DIP should cover various corner cases. -- Andrei
OK, thanks everyone. I thought DIPs were specifically for proposals that
had not been already approved.

-Steve
Andrei Alexandrescu via Digitalmars-d
2014-10-10 18:16:15 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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, will do. Everybody interested please chime in! -- Andrei
Brian Schott via Digitalmars-d
2014-10-10 20:29:41 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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.
IgorStepanov via Digitalmars-d
2014-10-10 21:19:20 UTC
Permalink
Post by Brian Schott via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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 =/
via Digitalmars-d
2014-10-10 20:44:09 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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.
IgorStepanov via Digitalmars-d
2014-10-10 21:30:52 UTC
Permalink
Post by via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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
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.
Steven Schveighoffer via Digitalmars-d
2014-10-10 20:47:45 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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
IgorStepanov via Digitalmars-d
2014-10-10 21:15:18 UTC
Permalink
On Friday, 10 October 2014 at 20:47:45 UTC, Steven Schveighoffer
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
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
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.
Steven Schveighoffer via Digitalmars-d
2014-10-10 21:26:49 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
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.
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.
struct A
{
int i;
alias i this;
}
struct C
{
A a;
string s;
alias a this;
alias s this;
}
void foo(T)(T t) if(is(T : int))
{
...
}
void foo(T)(T t) if(is(T : string))
{
...
}
C c;
foo(c); //Error: what do you mean: foo!(T : string) or foo!(T : int)
I agree with all this.
Post by IgorStepanov via Digitalmars-d
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
IgorStepanov via Digitalmars-d
2014-10-10 22:10:16 UTC
Permalink
On Friday, 10 October 2014 at 21:26:49 UTC, Steven Schveighoffer
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
On Friday, 10 October 2014 at 20:47:45 UTC, Steven
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
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.
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.
struct A
{
int i;
alias i this;
}
struct C
{
A a;
string s;
alias a this;
alias s this;
}
void foo(T)(T t) if(is(T : int))
{
...
}
void foo(T)(T t) if(is(T : string))
{
...
}
C c;
foo(c); //Error: what do you mean: foo!(T : string) or foo!(T
: int)
I agree with all this.
Post by IgorStepanov via Digitalmars-d
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.
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
Steven Schveighoffer via Digitalmars-d
2014-10-12 23:02:21 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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
You can write foo(c.shadow); This isn't hard.
Ok, I understood you, let's listen to what others say
Right, you can get around it.

But the issue here is, that I feel like is(T: U) means (from dlang.org):

is ( Type : TypeSpecialization )
The condition is satisfied if Type is semantically correct and it is the
same as or can be implicitly converted to TypeSpecialization.

This means is(C : int) should indicate that C can implicitly convert to
int. But in your DIP, it does not. I think this is incorrect.

-Steve
IgorStepanov via Digitalmars-d
2014-10-12 23:16:33 UTC
Permalink
On Sunday, 12 October 2014 at 23:02:13 UTC, Steven Schveighoffer
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
On Friday, 10 October 2014 at 21:26:49 UTC, Steven
Post by IgorStepanov via Digitalmars-d
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
You can write foo(c.shadow); This isn't hard.
Ok, I understood you, let's listen to what others say
Right, you can get around it.
But the issue here is, that I feel like is(T: U) means (from
is ( Type : TypeSpecialization )
The condition is satisfied if Type is semantically correct and
it is the same as or can be implicitly converted to
TypeSpecialization.
This means is(C : int) should indicate that C can implicitly
convert to int. But in your DIP, it does not. I think this is
incorrect.
-Steve
Hmm. I've written case (my previous post), when returning false
from is(T: S), where T has many pathes to S is dangerous. However
your words also contain the truth. I don't know what we need to
do. Maybe we should raise error during "is" semantic? Please,
read my example and say your opinion.
Steven Schveighoffer via Digitalmars-d
2014-10-13 00:54:13 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
You can write foo(c.shadow); This isn't hard.
Ok, I understood you, let's listen to what others say
Right, you can get around it.
is ( Type : TypeSpecialization )
The condition is satisfied if Type is semantically correct and it is
the same as or can be implicitly converted to TypeSpecialization.
This means is(C : int) should indicate that C can implicitly convert
to int. But in your DIP, it does not. I think this is incorrect.
Hmm. I've written case (my previous post), when returning false from
is(T: S), where T has many pathes to S is dangerous.
OK, I didn't understand your case before, but I just got it.

I understand what you mean, but this isn't anything new -- one can cause
weird problems by creating diamond-pattern interfaces also. I do not
actually think it is dangerous, because one would not leave an error
call in their code. So for a future change to a library to "mystically"
make a function start working is not a danger, because said code wasn't
sitting there broken in the first place.

I will note, that for diamond problem interfaces, the compiler seems to
take a different track than your DIP:

interface A {}
interface B : A {}
interface C : A {}

class X : B, C {}

static assert(is(X : A));

void main()
{
A a = new C; // works, not sure if it's B.A or C.A
}

I know this is a different problem -- we aren't pointing at two
different concrete implementations.

-Steve
IgorStepanov via Digitalmars-d
2014-10-13 11:38:46 UTC
Permalink
On Monday, 13 October 2014 at 00:54:13 UTC, Steven Schveighoffer
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
On Sunday, 12 October 2014 at 23:02:13 UTC, Steven
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
You can write foo(c.shadow); This isn't hard.
Ok, I understood you, let's listen to what others say
Right, you can get around it.
But the issue here is, that I feel like is(T: U) means (from
is ( Type : TypeSpecialization )
The condition is satisfied if Type is semantically correct
and it is
the same as or can be implicitly converted to
TypeSpecialization.
This means is(C : int) should indicate that C can implicitly
convert
to int. But in your DIP, it does not. I think this is
incorrect.
Hmm. I've written case (my previous post), when returning
false from
is(T: S), where T has many pathes to S is dangerous.
OK, I didn't understand your case before, but I just got it.
I understand what you mean, but this isn't anything new -- one
can cause weird problems by creating diamond-pattern interfaces
also. I do not actually think it is dangerous, because one
would not leave an error call in their code. So for a future
change to a library to "mystically" make a function start
working is not a danger, because said code wasn't sitting there
broken in the first place.
I will note, that for diamond problem interfaces, the compiler
interface A {}
interface B : A {}
interface C : A {}
class X : B, C {}
static assert(is(X : A));
void main()
{
A a = new C; // works, not sure if it's B.A or C.A
}
I know this is a different problem -- we aren't pointing at two
different concrete implementations.
-Steve
This is fundamentally different situation: interfaces haven't a
state, thus don't care what interface will be getted: B.C or C.C.
Moreover, we can think that we have only one base C (like virtual
inherited class in C++).
Alias this case requires a completely different approach.
IgorStepanov via Digitalmars-d
2014-10-12 23:22:13 UTC
Permalink
Advantage of ky way is a more strictness then your way: if
function with if(is(T: S)) will be called, error will be raised
at the first trying of convert T to S. And we don't give the
opportunity of possible error to spread away from the place of
origin.
IgorStepanov via Digitalmars-d
2014-10-10 21:18:04 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
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.
Walter Bright via Digitalmars-d
2014-10-10 21:11:05 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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!
Walter Bright via Digitalmars-d
2014-10-10 21:25:18 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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.
Timon Gehr via Digitalmars-d
2014-10-10 22:06:44 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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?
Walter Bright via Digitalmars-d
2014-10-10 22:29:51 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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.
Timon Gehr via Digitalmars-d
2014-10-10 22:46:29 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Walter Bright via Digitalmars-d
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);
}
Walter Bright via Digitalmars-d
2014-10-10 22:51:35 UTC
Permalink
Post by Timon Gehr via Digitalmars-d
Post by Walter Bright via Digitalmars-d
Post by Walter Bright via Digitalmars-d
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.
// ---
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.
IgorStepanov via Digitalmars-d
2014-10-10 23:25:35 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Timon Gehr via Digitalmars-d
Post by Walter Bright via Digitalmars-d
Post by Timon Gehr via Digitalmars-d
Post by Walter Bright via Digitalmars-d
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
// ---
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.
Symmetrically. You may use symbol from import, uncomment it in
aliased type and hijack it.
IgorStepanov via Digitalmars-d
2014-10-10 23:28:56 UTC
Permalink
BTW.

Overloaded functions in PR resolves un-properly (raises error
even if can be resolved correct function)
However it's easy to fix and I'll do it tomorrow.
IgorStepanov via Digitalmars-d
2014-10-10 22:07:59 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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)
Post by Steven Schveighoffer via Digitalmars-d
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.
Post by Steven Schveighoffer via Digitalmars-d
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?
Post by Steven Schveighoffer via Digitalmars-d
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?
Post by Steven Schveighoffer via Digitalmars-d
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?
Timon Gehr via Digitalmars-d
2014-10-10 22:18:35 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
Post by Walter Bright via Digitalmars-d
If 'a' and 'b' both contain overloads for function foo, then it should
behave like imports do (which is a bit complex).
The pseudo-code doesn't actually specify that the arguments the
identifier is being called with are considered at all.
Post by IgorStepanov via Digitalmars-d
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?
IgorStepanov via Digitalmars-d
2014-10-10 22:25:35 UTC
Permalink
Post by Timon Gehr via Digitalmars-d
On Friday, 10 October 2014 at 21:25:17 UTC, Walter Bright
Post by Walter Bright via Digitalmars-d
If 'a' and 'b' both contain overloads for function foo, then
it should
behave like imports do (which is a bit complex).
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?
...
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.
Timon Gehr via Digitalmars-d
2014-10-10 22:05:16 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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?)
IgorStepanov via Digitalmars-d
2014-10-10 22:20:55 UTC
Permalink
Post by Timon Gehr via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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:)
Post by Timon Gehr via Digitalmars-d
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
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
Post by Timon Gehr via Digitalmars-d
Furthermore, the following code compiles now, but doesn't under
the approach described in the DIP. Is this an actual regression
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?
Walter Bright via Digitalmars-d
2014-10-10 22:50:27 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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.
IgorStepanov via Digitalmars-d
2014-10-10 23:23:27 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
Post by Timon Gehr via Digitalmars-d
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?
long l = C.a();
long l = C.b();
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.
So it is.
Walter Bright via Digitalmars-d
2014-10-11 00:00:50 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
Post by Walter Bright via Digitalmars-d
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.
So it is.
Good!

The same rule applies for overloading.
IgorStepanov via Digitalmars-d
2014-10-11 14:23:12 UTC
Permalink
Post by Walter Bright via Digitalmars-d
On Friday, 10 October 2014 at 22:50:25 UTC, Walter Bright
Post by Walter Bright via Digitalmars-d
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.
So it is.
Good!
The same rule applies for overloading.
I've implemented overloading:
https://github.com/D-Programming-Language/dmd/pull/3998/files#diff-17b22eae29e74ce6ec29037438b5031cR2136

Please, tell me, what changes should I make to the DIP as a
result of yesterday's discussions.
And please, tell your opinion about "is" issue:

class A
{
int i;
alias i this;
}

class B
{
int i;
alias i this;
}

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

void foo(T)(T arg) if(is(T : int))
{
...
}

foo(C()); //Should it pass or not?
Walter Bright via Digitalmars-d
2014-10-12 04:31:09 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
Post by Walter Bright via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
Post by Walter Bright via Digitalmars-d
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.
So it is.
Good!
The same rule applies for overloading.
https://github.com/D-Programming-Language/dmd/pull/3998/files#diff-17b22eae29e74ce6ec29037438b5031cR2136
Please, tell me, what changes should I make to the DIP as a result of
yesterday's discussions.
At the very least, it should say it resolves ambiguities the same way that
imports and template mixins do.
Post by IgorStepanov via Digitalmars-d
class A
{
int i;
alias i this;
}
class B
{
int i;
alias i this;
}
class C
{
A a;
B b;
alias a this;
alias b this;
}
void foo(T)(T arg) if(is(T : int))
{
...
}
foo(C()); //Should it pass or not?
There's a rule with imports that if the same symbol is reachable via multiple
paths through the imports, that it is not an ambiguity error. Here, the same
type is reachable through multiple alias this paths, so by analogy it shouldn't
be an error.
via Digitalmars-d
2014-10-12 08:36:03 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Timon Gehr via Digitalmars-d
class A
{
int i;
alias i this;
}
class B
{
int i;
alias i this;
}
class C
{
A a;
B b;
alias a this;
alias b this;
}
void foo(T)(T arg) if(is(T : int))
{
...
}
foo(C()); //Should it pass or not?
There's a rule with imports that if the same symbol is
reachable via multiple paths through the imports, that it is
not an ambiguity error. Here, the same type is reachable
through multiple alias this paths, so by analogy it shouldn't
be an error.
It's the same type, but different symbols; actual accesses would
be ambiguous. `is(T : int)` shouldn't evaluate to true if `int a
= T.init;` would fail.
IgorStepanov via Digitalmars-d
2014-10-12 11:45:55 UTC
Permalink
Post by via Digitalmars-d
Post by Walter Bright via Digitalmars-d
Post by Timon Gehr via Digitalmars-d
class A
{
int i;
alias i this;
}
class B
{
int i;
alias i this;
}
class C
{
A a;
B b;
alias a this;
alias b this;
}
void foo(T)(T arg) if(is(T : int))
{
...
}
foo(C()); //Should it pass or not?
There's a rule with imports that if the same symbol is
reachable via multiple paths through the imports, that it is
not an ambiguity error. Here, the same type is reachable
through multiple alias this paths, so by analogy it shouldn't
be an error.
It's the same type, but different symbols; actual accesses
would be ambiguous. `is(T : int)` shouldn't evaluate to true if
`int a = T.init;` would fail.
I found an example of a situation that is bothering me.
Let we have a persistence framework, which provides a storing D
object in some persistence storage: DB, file et c.

In introduces paired functions store/load and special type
PersistenceObject.

If stored type is subtype of PersistenceObject it converts to
PersistenceObject and PersistenceObject.load(stream) called for
loading object (and PersistenceObject.store(stream) for storing).
Otherwice if object can't be converted to PersistenceObject it
should be serialized via "serialize" function (or deserialized
via "deserialize").

struct PersistenceFramework
{
void store(T)(T arg) if (is(T : PersistenceObject))
{
PersistenceObject po = arg;
arg.store(stream);
}

void store(T)(T arg) if (!is(T : PersistenceObject))
{
PersistenceObject po = arg;
store(serialize(arg));
}

void load(T)(ref T arg) if (is(T : PersistenceObject))
{
PersistenceObject po = arg;
arg.load(stream);
}

void load(T)(ref T arg) if (!is(T : PersistenceObject))
{
PersistenceObject po = arg;
load(serialize(arg));
}

Stream stream;
}

/****************************************************************
And we have the next types which we want to store and load
*****************************************************************/

struct Role
{
...
}

struct User
{
Role role;
PersistenceObject po;
//...
alias role this;
alias po this;
}

/*****************************************************************/

User u;
persistenceFramework.load(u)
//...
persistenceFramework.store(u);


/******************************************************************/
Role is not subtype of PersistenceObject thus all works ok.
We can store User via User.po and load it again;

Some time later, Role designer decided that Role should be
subtype of PersistenceObject and changed Role definition:

struct Role
{
...
PersistenceObject po;
alias po this;
}

Now, User can not be converted to PersistenceObject because there
are two path to convert: User.po and User.role.po;
Storing code after this change will be copiled successfully (if
we follow your "is" rule), however object will be tried to load
via "void load(T)(ref T arg) if (!is(T : PersistenceObject))".
Because object was saved via "void store(T)(T arg) if (is(T :
PersistenceObject))" at the previous program run, user will not
be loaded succesfully. Moreover, you will get an strange
unexpected program behaviour and will be hard to find real error
cause.

/*****************************************************************/
And finally, if you want to check, if you Type _really_ can be
converted to AnotherType, you can use the next check:

void foo(Type)(Type arg) if (is(typeof({AnotherType x =
Type.init;})))
{

}
Daniel N via Digitalmars-d
2014-10-13 15:21:31 UTC
Permalink
Post by Timon Gehr via Digitalmars-d
class A
{
int i;
alias i this;
}
class B
{
int i;
alias i this;
}
class C
{
A a;
B b;
alias a this;
alias b this;
}
My preferred solution would be to reject the 2nd alias
declaration outright.

I don't see any value in intentionally creating the above
pattern, _if_ it occurs then it's most likely due to an
unintentional side-effect of a re-factoring, thus it should error
out as close as possible to the real error.
IgorStepanov via Digitalmars-d
2014-10-14 12:33:48 UTC
Permalink
Post by Daniel N via Digitalmars-d
Post by Timon Gehr via Digitalmars-d
class A
{
int i;
alias i this;
}
class B
{
int i;
alias i this;
}
class C
{
A a;
B b;
alias a this;
alias b this;
}
My preferred solution would be to reject the 2nd alias
declaration outright.
I don't see any value in intentionally creating the above
pattern, _if_ it occurs then it's most likely due to an
unintentional side-effect of a re-factoring, thus it should
error out as close as possible to the real error.
This code tell that C is subtype of A and C is subtype of B.
User can use this fact in his code:
void foo(B);

C c = new C;
foo(c); //Ok.
Of course, we shouldn't allow user to cast c to int:
int i = c; //wrong
However, user can explicitly cast c to his subtype, which is
convertable to int:
int i = cast(B)c; //Ok
Summarizing, I disagree with suggestion disallow this code at
type semantic stage.
Dicebot via Digitalmars-d
2014-10-15 02:46:04 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
This code tell that C is subtype of A and C is subtype of B.
void foo(B);
C c = new C;
foo(c); //Ok.
int i = c; //wrong
However, user can explicitly cast c to his subtype, which is
int i = cast(B)c; //Ok
Summarizing, I disagree with suggestion disallow this code at
type semantic stage.
I agree. It will also make possible to break already working
disambugation of `foo(c)` kind but adding new `alias this` to one
of subtypes independently. That sounds annoying.
Daniel N via Digitalmars-d
2014-10-15 03:49:40 UTC
Permalink
Post by Dicebot via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
This code tell that C is subtype of A and C is subtype of B.
void foo(B);
C c = new C;
foo(c); //Ok.
int i = c; //wrong
However, user can explicitly cast c to his subtype, which is
int i = cast(B)c; //Ok
Summarizing, I disagree with suggestion disallow this code at
type semantic stage.
I agree. It will also make possible to break already working
disambugation of `foo(c)` kind but adding new `alias this` to
one of subtypes independently. That sounds annoying.
I guess the best part of D is, you have the means to fix anything
you disagree with yourself... I can add a static assert to my
class and be happy.

I have another idea, we could define that the shortest conversion
chain wins, analogous to type promotions, that makes it possible
to contain the issue inside C.

class C
{
A a;
B b;

int disambiguate_int()
{
return a;
}
alias a this;
alias b this;
alias disambiguate_int this;
static assert(__traits(compiles, {int _ = C.init;}), "Ambiguous
alias this");
}

i.e. this assert should pass.
IgorStepanov via Digitalmars-d
2014-10-15 09:50:16 UTC
Permalink
Post by Daniel N via Digitalmars-d
On Tuesday, 14 October 2014 at 12:33:50 UTC, IgorStepanov
Post by IgorStepanov via Digitalmars-d
This code tell that C is subtype of A and C is subtype of B.
void foo(B);
C c = new C;
foo(c); //Ok.
int i = c; //wrong
However, user can explicitly cast c to his subtype, which is
int i = cast(B)c; //Ok
Summarizing, I disagree with suggestion disallow this code at
type semantic stage.
I agree. It will also make possible to break already working
disambugation of `foo(c)` kind but adding new `alias this` to
one of subtypes independently. That sounds annoying.
I guess the best part of D is, you have the means to fix
anything you disagree with yourself... I can add a static
assert to my class and be happy.
I have another idea, we could define that the shortest
conversion chain wins, analogous to type promotions, that makes
it possible to contain the issue inside C.
class C
{
A a;
B b;
int disambiguate_int()
{
return a;
}
alias a this;
alias b this;
alias disambiguate_int this;
static assert(__traits(compiles, {int _ = C.init;}),
"Ambiguous alias this");
}
i.e. this assert should pass.
In first edition I've implemented rule, when if type defines
alias this directly, this alias hides all indirect aliases with
the same type. However Andrey said that we should implement the
strictest rules as possible and maybe relax them later.
Thus I implemented current rules, but saved the old implemetation
of search. After some time we will able to return to first rule.
It's not hard.

Joseph Rushton Wakeling via Digitalmars-d
2014-10-10 22:27:49 UTC
Permalink
Post by IgorStepanov via Digitalmars-d
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.
Walter Bright via Digitalmars-d
2014-10-10 22:52:56 UTC
Permalink
Post by Joseph Rushton Wakeling via Digitalmars-d
Post by IgorStepanov via Digitalmars-d
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.
Post by Joseph Rushton Wakeling via Digitalmars-d
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.
Jacob Carlborg via Digitalmars-d
2014-10-11 10:42:29 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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'm not so sure about that. Perhaps it makes it more understandable for
a language designer. But not for a user. You try to call a function but
you get a conflict with a private symbol. The user will get frustrated
thinking: "stupid compiler, of course I want to call the public function".
--
/Jacob Carlborg
Walter Bright via Digitalmars-d
2014-10-12 04:28:25 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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'm not so sure about that. Perhaps it makes it more understandable for a
language designer. But not for a user. You try to call a function but you get a
conflict with a private symbol. The user will get frustrated thinking: "stupid
compiler, of course I want to call the public function".
The theory is that simpler rules are better than complex rules, even if the
simpler rules aren't always ideal.
Timon Gehr via Digitalmars-d
2014-10-12 12:34:25 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Walter Bright via Digitalmars-d
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'm not so sure about that. Perhaps it makes it more understandable for a
language designer. But not for a user. You try to call a function but you get a
conflict with a private symbol. The user will get frustrated thinking: "stupid
compiler, of course I want to call the public function".
The theory is that simpler rules are better than complex rules, even if
the simpler rules aren't always ideal.
Public symbols conflicting with private symbols are not just not ideal,
they are a major PITA. The procedure for resolving ambiguities using
alias introduces new private symbols itself!
Joseph Rushton Wakeling via Digitalmars-d
2014-10-12 11:27:54 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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.
That's fine. I just wanted to be sure that there wasn't a risk of multiple
alias this breaking in unpleasant ways, if/when
https://issues.dlang.org/show_bug.cgi?id=10996 is fixed.
Continue reading on narkive:
Loading...