View mode: basic / threaded / horizontal-split · Log in · Help
January 09, 2006
Final method in interface causes error
I'm hoping someone can explain the following behavior to me.  If H.get() is declared final, I get a linker error.  It has to do with calling that.get() in opEquals where "that" is some subtype of H.  Thanks in advance for any clarification.

Regards,
Garett

------------
module test;
private import std.stdio;

interface H {
 //final void get(out int i); // Error 42: Symbol Undefined _D4test1H3getFJiZv
         void get(out int i); // OK
   final bool opEquals(H that);
}

interface I : H {
   void set(int i);
}

class Foo : I {
   private int i;

   final void get(out int i) { i = this.i; }

   final void set(int i) { this.i = i; }

   final bool opEquals(H that) {
       if (that is null) return false;
       int this_i; this.get(this_i);
       int that_i; that.get(that_i);
       return this_i == that_i;
   }
}

int main(char[][] args) {
   auto Foo foo = new Foo;
   return 0;
}
January 09, 2006
Re: Final method in interface causes error
Garett Bass wrote:
> I'm hoping someone can explain the following behavior to me.  If H.get() 
> is declared final, I get a linker error.  It has to do with calling 
> that.get() in opEquals where "that" is some subtype of H.  Thanks in 
> advance for any clarification.
> 
> Regards,
> Garett
> 
> ------------
> module test;
> private import std.stdio;
> 
> interface H {
>  //final void get(out int i); // Error 42: Symbol Undefined 
> _D4test1H3getFJiZv
>          void get(out int i); // OK
>    final bool opEquals(H that);
> }
> 
> interface I : H {
>    void set(int i);
> }
> 
> class Foo : I {
>    private int i;
> 
>    final void get(out int i) { i = this.i; }
> 
>    final void set(int i) { this.i = i; }
> 
>    final bool opEquals(H that) {
>        if (that is null) return false;
>        int this_i; this.get(this_i);
>        int that_i; that.get(that_i);
>        return this_i == that_i;
>    }
> }
> 
> int main(char[][] args) {
>    auto Foo foo = new Foo;
>    return 0;
> }

You're just coming up with all kinds of odd code for me to check out! =P

I don't think final methods apply to interfaces, and this is also 
another odd bug that no one would've considered.
January 09, 2006
Re: Final method in interface causes error
James Dunne wrote:
> You're just coming up with all kinds of odd code for me to check out! =P
> 
> I don't think final methods apply to interfaces, and this is also 
> another odd bug that no one would've considered.

James, thanks for your consideration.  I don't see why it should be so odd.  It seems desirable for an interface to specify the bare minimum method signature of the class it expects you to implement.  I expect classes deriving from H, directly or indirectly through I, to disallow methods get and opEquals from being overridden.  Effectively I'm allowing subclasses of Foo to intercept calls to set(), but not other calls.

I tried this in Java, and it turns out that final is not permitted in a Java interface.  This led me to believe that final is not enforced in D interfaces, so I tried overriding H's final opEquals (new code below), and the compiler thought that was just fine.  Note the contradictory output for opEquals.

I'm pretty sure there is a bug in here somewhere, but I'm not sure where.  Some possibilities include:

   1. final should not be allowed in an interface.
      A compiler error and some documentation would
      help us avoid some uncertainty here.

   2. final should be enforced by an interface.
      In this case, Error 42 should not appear below.

Advice on submitting this as a bug?

Curiouser and curiouser...
Garett

------------
module test;
private import std.stdio;

interface I {
 //final void get(out int i); // Error 42: Symbol Undefined _D4test1H3getFJiZv
         void get(out int i); // OK
         void set(int i);
   final bool opEquals(I that);
}

class Foo : I {
   private int i;

   final void get(out int i) { i = this.i; }

   final void set(int i) { this.i = i; }

   bool opEquals(I that) {
       if (that is null) return false;
       int this_i; this.get(this_i);
       int that_i; that.get(that_i);
       return this_i == that_i;
   }
}

class Bar : Foo {
   bool opEquals(I that) {
       return false;
   }
}

int main(char[][] args) {
   auto Foo foo = new Foo;
   auto Bar bar = new Bar;

   writefln("foo == bar = %b", foo == bar); // true
   writefln("bar == foo = %b", bar == foo); // false
   return 0;
}
January 09, 2006
Re: Final method in interface causes error
"Garett Bass" <garettbass@studiotekne.com> wrote in message 
news:dpu92h$1lv$1@digitaldaemon.com...
>    2. final should be enforced by an interface.
>       In this case, Error 42 should not appear below.

Does final in an interface make much sense?  A final method is one which 
cannot be overridden.  If you have no body for a method, as in an interface, 
what point is there in not allowing it to be overridden in order to give it 
one?

>    1. final should not be allowed in an interface.
>       A compiler error and some documentation would
>       help us avoid some uncertainty here.

I think this is the problem here.  D seems to have a generalized inheritance 
methodology, but final just doesn't apply to interfaces.  So the compiler 
thinks a final interface method is just dandy, but the linker doesn't find a 
body for it, so it complains about an undefined reference.  The compiler 
_should_ catch this.
January 09, 2006
Re: Final method in interface causes error
Jarrett Billingsley wrote:
> "Garett Bass" <garettbass@studiotekne.com> wrote in message 
> news:dpu92h$1lv$1@digitaldaemon.com...
> 
>>   2. final should be enforced by an interface.
>>      In this case, Error 42 should not appear below.
> 
> 
> Does final in an interface make much sense?  A final method is one which 
> cannot be overridden.  If you have no body for a method, as in an interface, 
> what point is there in not allowing it to be overridden in order to give it 
> one?
> 
> 
>>   1. final should not be allowed in an interface.
>>      A compiler error and some documentation would
>>      help us avoid some uncertainty here.
> 
> 
> I think this is the problem here.  D seems to have a generalized inheritance 
> methodology, but final just doesn't apply to interfaces.  So the compiler 
> thinks a final interface method is just dandy, but the linker doesn't find a 
> body for it, so it complains about an undefined reference.  The compiler 
> _should_ catch this. 
> 
> 

Agreed 100%.  I was gonna say this exactly if I had replied before you.

Well, one now has to ask the question, "Does a final method in an 
interface actually mean something?"  In Garrett's position, he would 
logically think that 'final' in that context is acting as a special 
contract of sorts.  He wants classes inheriting that interface to be 
forced to 'final'-ize those methods so that no subclass of that 
implementing class can override them.

This doesn't sound all to unreasonable, but then come in the arguments 
about semantic consistency across keywords.  However, this falls apart 
miserably due to other keywords having multiple semantic meanings in 
different cases already in the language (e.g. const in C++, auto in D, 
static in D).

Now, if one wants to propose allowing this behavior (allowing final as a 
contract in interfaces) as a language extension then he/she must 
determine if the use of final here is orthogonal to classes.  I'm fairly 
certain it is, because final as it stands now in D (disallowing 
overriding of methods in classes) doesn't make much sense in the 
interface context since you'll have a useless method in an interface 
which no one can override.  These new semantics will surely not break 
any code and will actually have value.

It's got my vote.  I'm just a little confused on how/when one would use 
such a feature, but obviously Garrett's got an assumedly valid sample case.
Top | Discussion index | About this forum | D home