Thread overview
alias declarations revisited
Jan 11, 2004
Manfred Nowak
Jan 11, 2004
Ben Hinkle
Jan 13, 2004
Manfred Nowak
Jan 13, 2004
J Anderson
January 11, 2004
http://www.digitalmars.com/d/declaration.html, [cited 11.01.04]:
| class C : B {
|   int foo( int a ) { return 3; }
|   alias B.foo foo;
| }

This looks like a bug to me. At least it fells into the categories `unused variable, unreachable code, x=x' and therefore a maintenance problem.

The declaration `class C : B' denotes, that C inherets every function of B by definition. Therefore the alias declaration in this code is at least completely superfluous.


The feeling that it should be a bug stems from the fact, that B.foo is
a set of functions, namely B.foo and A.foo, which is inhereted into B,
and therefore A.foo is reintroduced into the current scope, despite of
the fact that it has been just overloaded. But then I read the
introductory words again:
| Aliases can also 'import' a set of overloaded functions, that can
| be overloaded with functions in the current scope

This seems to mean, that alias declarations are of lower priority and that the textual order of aliasing/overloading does not matter. If so, then it imposes a recognition problem: after reading some overloading the maintainer eventually reaches an alias declaration that 'imports' overloadable functions. Those functiomns, that are not already overloaded expand the current scope at least temporarily. Therefore a maintainer must recall all already declared functions in the current scope to determine which of the functions introduced by the alias are really expanding the current scope. This holds for every newly found alias declaration.

To avoid this the alias declarations must come first, because they are of lower priority, than the declarations in the current scope.


But first have a look at the current implementation, when A.foo is
overloaded in B:
<code>
	class A {
	    int foo(int a) { return 1; }
	}

	class B : A {
	    int foo(int a) { return 4; }  // A.foo overloaded
	    int foo( int a, uint b ) { return 2; }
	}

	class C : B {
	    int foo( int a ) { return 3; }
	    alias B.foo foo;
	}

	class D : C  {
	}


	void test()
	{
	    D b = new D();
	    int i;

	    i = b.foo(1, 2u);	// calls B.foo
         printf("%d\n",i);
	}

void main()
{
  test();
}
</code>

The code compiles and the printing consists of the expected number `2'.
But oops, the call of `i = b.foo(1);' was lost. Reintroducing it:
<code> [...]
	void test()
	{
	    D b = new D();
	    int i;

	    i = b.foo(1, 2u);	// calls B.foo
         i = b.foo(1);       // lost code reintroduced here
         printf("%d\n",i);
	}
[...] </code>

yields the compiler error:
| test.d(25): function foo overloads int(int a) and int(int a) both
| match argumentlist for foo

This is inacceptable. Usually the class designers are not identical with the users of the class. Therefore this behaviour of the compiler is equivalent to a qualitiy control by the consumer. And in addition, there should be no error message at all, because the questionable function was overloaded in the current 'importing' scope.


Now lets change the textual order of the alias and the overloading.
<code> [...]
	class C : B {
	    alias B.foo foo;
	    int foo( int a ) { return 3; }
	}
[...]</code>

This yields the compiler error:
| test.d(6): function foo conflicts with C.foo at test.d(12)

B.foo(int) conflicts with its overloading function C.foo(int)? According to the introductuary words this should not happen at all, but the error message is presented before the actual call of the consumer and that is clearly better.


Conclusion: the actual behaviour of the compiler is inconsistent with the supposed meaning of the documentation.


IMO the supposed meaning of the documentation should guide the implementation.


Moreover, if the supposed meaning takes the lead, then D would have a
nice method of contructing new classes. Example:
<code>
class A1 {
  int foo(int a){return 1;};
}

class A2 : A1 {
  int foo(int a, uint b){return 2;}
}

class B1 {
  uint bar(int a){return 3;};
}

class B2 : B1 {
  int bar(int a, uint b){return 4;}
}

class C {
  alias A2.foo xxx;
  alias B2.bar xxx;                 //A2.foo conflicts with B2.bar

  int xxx(int a, uint b){return 5;} //conflict resolved by overloading
}                                   //C contains A1.foo, B1.bar, C.xxx

class D : C {
}

void test()
{
  D b = new D();
  int i;

  i = b.xxx(1, 2u);	// calls C.xxx
  printf("%d\n",i);
}

void main()
{
  test();
}
</code>

But currently the compiler message is:
| test.d(14): function bar conflicts with C.xxx at test.d(21)

I.e. B2.bar conflicts with C.xxx. And when omitting the overloading the underlying conflict is only detected when a consumer tries to call the conflictuary function. :-(


So long.

-- 
Fight Spam! Join EuroCAUCE: http://www.euro.cauce.org/ 2EA56D6D4DC41ABA311615946D3248A1
January 11, 2004
"Manfred Nowak" <svv1999@hotmail.com> wrote in message news:btr852$2d1q$1@digitaldaemon.com...
> http://www.digitalmars.com/d/declaration.html, [cited 11.01.04]:
> | class C : B {
> |   int foo( int a ) { return 3; }
> |   alias B.foo foo;
> | }
>
> This looks like a bug to me. At least it fells into the categories `unused variable, unreachable code, x=x' and therefore a maintenance problem.
>
> The declaration `class C : B' denotes, that C inherets every function of B by definition. Therefore the alias declaration in this code is at least completely superfluous.

D is like C++ in this case. Java allows inherited methods to be involved in
overload resolution but C++ doesn't. I think the reason C++ doesn't is that
overloading can be surprising when combined with implicit type conversions,
causing maintenance problems of its own. Example:
class B {
  void foo(short a) {...}
}
class C : B {
  void foo(int a) {...}
  void bar() {
    short x=2;
    foo(x);
    }
}
Unless you go poking around in B you'd think that bar would call C.foo but
if inherited methods took part in overloading then it would call B.foo. When
you explicitly use an alias to bring in the other definition of foo then
someone reading C will know to go look in B for more foo definitions. This
example seems pretty trivial but imagine if there were 4 or 5 layers of
inheritance between B and C.

Then again, I'm with you that D should use the Java model - overload with abandon. :-)

-Ben


January 13, 2004
Ben Hinkle wrote:

[...]
> causing maintenance problems of its own. Example:
[...]

What is wrong with dmd? Trying to test your code completed with a
main() function:
<code>
class B {
  void foo(short a) {}
}
class C  {
  void foo(int a) {}

  void bar() {
    short x=2;
    foo(x);
  }
}
void main()
{
  C c;

  c.bar();
}
</code>

It compiles but produces a runtime-error: Access Violation

So long.
-- 
Fight Spam! Join EuroCAUCE: http://www.euro.cauce.org/ 2EA56D6D4DC41ABA311615946D3248A1
January 13, 2004
Manfred Nowak wrote:

>Ben Hinkle wrote:
>
>[...]
>  
>
>>causing maintenance problems of its own. Example:
>>    
>>
>[...]
>
>What is wrong with dmd? Trying to test your code completed with a main() function:
><code>
>class B {
>  void foo(short a) {}
>}
>class C  {
>  void foo(int a) {}
>   void bar() {
>    short x=2;
>    foo(x);
>  }
>}
>void main()
>{
>  C c;
>
>  c.bar();
>}
></code>
>
>It compiles but produces a runtime-error: Access Violation
>
>So long.
>  
>
//That should be

class B {
 void foo(short a) {}
}
class C  {
 void foo(int a) {}
  void bar() {
   short x=2;
   foo(x);
 }
}
void main()
{
 C c = new C;

 c.bar();
}