www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Final method in interface causes error

reply Garett Bass <garettbass studiotekne.com> writes:
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;
}
Jan 09 2006
parent reply James Dunne <james.jdunne gmail.com> writes:
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.
Jan 09 2006
parent reply Garett Bass <garettbass studiotekne.com> writes:
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; }
Jan 09 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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.
Jan 09 2006
parent James Dunne <james.jdunne gmail.com> writes:
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.
Jan 09 2006