www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 2524] New: final override inconsistent when implementing interfaces

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524

           Summary: final override inconsistent when implementing interfaces
           Product: D
           Version: 1.038
          Platform: PC
        OS/Version: Windows
            Status: NEW
          Keywords: rejects-valid, spec
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla digitalmars.com
        ReportedBy: schveiguy yahoo.com


The following code builds:

interface I
{
  void foo();
}

class C : I
{
  override void foo();
}

But the following does not work:

class C : I
{
  final override void foo();
}

testfinaloverride.d(8): function testfinaloverride.C.foo does not override any
function

The two cases should be consistent.  The spec is unclear to me as to whether
implementing an interface function qualifies as overriding a function or not.

I would vote for the case where override requires either to override a base
class function or implement an interface function, since it is a pain if you
change a base class to an interface, you'd have to remove all the override
keywords.  So I'm marking it as rejects-valid.

The spec should also specifically lay out what override does in the case of
interfaces.


-- 
Dec 18 2008
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524






It compiles in D2, though silent interface method implementation looks strange
for me.


-- 
Dec 19 2008
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524


burton-radons shaw.ca changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |burton-radons shaw.ca






 It compiles in D2, though silent interface method implementation looks strange
 for me.
I've tested this with DMD 2.022 and 2.021 and I get this error in both cases. --
Jan 01 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524


schveiguy yahoo.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |samukha voliacable.com





*** Bug 2593 has been marked as a duplicate of this bug. ***


-- 
Jan 19 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524






My report is also a duplicate of this bug.
Note that Walter closed it with the following comment:

«A "final private" method is not virtual, and hence won't work for an
interface
method. That's why the error message appears. You can make it an enhancement
request if you like.»


-- 
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524


2korden gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |2korden gmail.com





*** Bug 2538 has been marked as a duplicate of this bug. ***


-- 
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524







private" that makes the method non-virtual. Implementing interface methods with
final methods is absolutely legal.


-- 
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524






In fact, private implies final (non-virtual), and does not put the function in
the vtable (meaning it cannot override a base function).  From the spec: "All
non-static non-private non-template member functions are virtual."  If someone
casts your class to an interface, do you want them to now be able to call your
private function?


-- 
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524







 In fact, private implies final (non-virtual), and does not put the function in
 the vtable (meaning it cannot override a base function).  From the spec: "All
 non-static non-private non-template member functions are virtual."
The sentence you've quoted states nothing about functions that _are_ private. Moreover, a final method _can_ override a method in a base class. Even so, an interface has its own vtable, so the virtuality or not of a function with respect to its class (or a class from which it is derived) should have nothing to do with whether it can implement an interface method.
 If someone casts your class to an interface, do you want them to 
 now be able to call your private function?
Probably not. But the way it's attempted in issue 2538 brings us back to the problem of inheritance protection. But we can talk about this there. --
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524







 In fact, private implies final (non-virtual), and does not put the function in
 the vtable (meaning it cannot override a base function).
Protection should be orthogonal to "virtuality" attributes. Here is an example: class A { private void foo() { writefln("A.foo"); } } class B : A { private override void foo() { super.foo(); writefln("B.foo"); } }
 From the spec: "All non-static non-private non-template member functions are
virtual."
I believe this is a design flow. Same as "package implies final".
 If someone casts your class to an interface, do you want them to now
 be able to call your private function?
 
Of course, why would you inherit or override it other than to allow virtual behavior? Here is an example: interface INetworkListener { void acceptNetworkPacket(); } class NetworkManager { static void registerNetworkListener(INetworkListener listener) { ... } } class SoundManager : private INetworkListener { this() { NetworkManager.registerNetworkListener(this); } private void acceptNetworkPacket() { // ... } } No-one should know that SoundManager implements INetworkListener interface. Since "acceptNetworkPacket" method is an implementation detail, I don't want it to be visible from the outside of this class (to prevent accidential invokation). Thus I mark it private. Note that private interface inheritance is harmless as it doesn't shadow Object's methods (opCmp, opEquals, toString etc). --
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524






But I can still cast to the interface.  Protection attributes are compile-time
entities, they are not flagged at runtime:

SoundManager s;
Object o = s;

INetworkListener inl = cast(INetworkListener)o;

The compiler just looks in the object's list of interfaces to see if it finds
that interface.  There is no protection associated with it at runtime.

What you are asking for requires a major compiler redesign for limited value. 
You can easily implement what you want using a private inner class:

interface INetworkListener {
    void acceptNetworkPacket();
}

class NetworkManager
{
    static void registerNetworkListener(INetworkListener listener) { ... }
}

class SoundManager
{
    private class NetworkListener : INetworkListener
    {
      void acceptNetworkPacket() {
        // ...
      }
    }

    this() {
        NetworkManager.registerNetworkListener(new NetworkListener);
    }
}

This is similar to how Java works with anonymous classes.  D can do the same
thing I think, but I can't remember the syntax right now.


-- 
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524







 But I can still cast to the interface.  Protection attributes are compile-time
 entities, they are not flagged at runtime:
 
 SoundManager s;
 Object o = s;
 
 INetworkListener inl = cast(INetworkListener)o;
 
 The compiler just looks in the object's list of interfaces to see if it finds
 that interface.  There is no protection associated with it at runtime.
 
So what? How does this differ from the following in C++: class A { void foo(); } class B : protected A { } B* b = new B(); //b->foo(); // error //A* a = b; // error void* o = b; A* a = (A*)o; a->foo(); You just hijacked type system, that's it. One possible solution would be to add protection flag into class typeinfo which will be taken into account during dynamic cast. Or remove interface from classinfo.interfaces altogether (it should still be possible to cast known class to its known base class/interface statically, i.e. without dynamic cast). I know the issue is not of high value but it should either be fixed for consistency, or protection inheritance attributes should be removed from language. --
Jan 20 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524







 But I can still cast to the interface.  Protection attributes are compile-time
 entities, they are not flagged at runtime:
 
 SoundManager s;
 Object o = s;
 
 INetworkListener inl = cast(INetworkListener)o;
 
 The compiler just looks in the object's list of interfaces to see if it finds
 that interface.  There is no protection associated with it at runtime.
But the compiler is capable of checking to see whether the inheritance is public or private. It just doesn't at the moment. No runtime protection checking needed in this instance.
 What you are asking for requires a major compiler redesign for limited value. 
I still think this "major compiler redesign" should be removing inheritance protection altogether. Which would have the value of simplifying the language by getting rid of these things that don't make sense.
 So what? How does this differ from the following in C++:
 
 class A {
     void foo();
 }
 
 class B : protected A {
 }
 
 B* b = new B();
 //b->foo(); // error
 //A* a = b; // error
 void* o = b;
 A* a = (A*)o;
 a->foo();
Firstly, try putting your code through a C++ compiler. Secondly, your code goes through void*, whereby it's obvious that you're intent on subverting the type system. Moreover, it may break if multiple inheritance is involved. That said, as I try it C++ allows the cast straight from B* to A*, but it must be explicit. --
Jan 21 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524








 But I can still cast to the interface.  Protection attributes are compile-time
 entities, they are not flagged at runtime:
 
 SoundManager s;
 Object o = s;
 
 INetworkListener inl = cast(INetworkListener)o;
 
 The compiler just looks in the object's list of interfaces to see if it finds
 that interface.  There is no protection associated with it at runtime.
But the compiler is capable of checking to see whether the inheritance is public or private. It just doesn't at the moment. No runtime protection checking needed in this instance.
Casting to Object is valid, since the class did not inherit from Object privately. Once you get to object, you need runtime information to cast to a subclass or interface. If you're suggesting that an optimization can "see" that the object originated from a SoundManager instance, then I can easily generate a case where the cast to object and the cast to the interface are hidden in opaque functions, eliminating the possibility of optimization. I still contend that this cannot be checked at compile-time.
 So what? How does this differ from the following in C++:
 
 [snip]

 You just hijacked type system, that's it.
No, I did not. Casting to an Object is a legal move within the type system. It is equivalent to casting to a base class. Casting to an interface is also a legal move, it is like a C++ dynamic_cast to a derived class. There is no hijacking going on.
 
 One possible solution would be to add protection flag into class typeinfo which
 will be taken into account during dynamic cast. Or remove interface from
 classinfo.interfaces altogether (it should still be possible to cast known
 class to its known base class/interface statically, i.e. without dynamic cast).
I'm not sure this is possible, but I don't know completely the inner workings of an interface cast. I think it's required that the interface vtable be present in the class instance, and I think it's probably also required that it be in the classinfo.
 I know the issue is not of high value but it should either be fixed for
 consistency, or protection inheritance attributes should be removed from
 language.
I would agree wholeheartedly that inheritance protection should be disallowed for interfaces. I am not sure about inheritance protection for the base class, I've never used it, and the documentation is completely absent (save for the mention that it's valid syntax). I can't see a use case that couldn't be solved by composition, or using private inner classes. --
Jan 21 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524







 But the compiler is capable of checking to see whether the inheritance is 
 public or private.  It just doesn't at the moment.  No runtime protection 
 checking needed in this instance.
Casting to Object is valid, since the class did not inherit from Object privately. Once you get to object, you need runtime information to cast to a subclass or interface.
Sorry, I misread what your code was doing. It's the same as the C++ example indeed. --
Jan 21 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524


Walter Bright <bugzilla digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla digitalmars.com



21:27:32 PST ---
This is a compiler bug. You can override an interface function.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 22 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524




Commit pushed to https://github.com/D-Programming-Language/dmd

https://github.com/D-Programming-Language/dmd/commit/f595bde35576dd0165dd0e0964d478c7db31955a
fix Issue 2524 - final override inconsistent when implementing interfaces

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 22 2012
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2524


Walter Bright <bugzilla digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |FIXED


-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 22 2012