On Wednesday, 2 September 2015 at 18:57:11 UTC, Jacob Carlborg wrote:
I encountered a problem in the implementation of std.xml.Document.opEquals (yes, I've reported an issue). The problem is demonstrated with this example:

class Base
{
    int a;

    override bool opEquals (Object o)
    {
        if (auto base = cast(Base) o)
            return base.a == a;
        else
            return false;
    }
}

class Foo : Base
{
    int b;

    override bool opEquals (Object o)
    {
        if (auto foo = cast(Foo) o)
            return super == cast(Base) foo && foo.b == b;
        else
            return false;
    }
}

void main()
{
    auto f1 = new Foo;
    auto f2 = new Foo;
    assert(f1 == f2);
}

This code will result in an infinite recursion. I think the problem is in the super call, due to == being rewritten to call object.opEquals. The implementation of object.opEquals will call opEquals on the actual instances. The call will be dynamically resolved and end up calling Foo.opEquals instead of Base.opEquals.

Is this really good behavior, something a developer would expect? I mean, in every other case calling super.someMethod will actually call the method in the base class.

In this case the solution/workaround is to explicitly call super.opEquals, but that will miss some optimizations implemented in object.opEquals.

Yeah, I would just call super.opEquals, like so.

class Base {
    int a;

    override bool opEquals(Object o) {
        if (auto other = cast(Base) o)
            return a == other.a;

        return false;
    }
}

class Foo : Base {
    int b;

    override bool opEquals(Object o) {
        if (!super.opEquals(o))
            return false;

        if (auto other = cast(Foo) o)
            return b == other.b;

        return false;
    }
}

void main()
{
    auto f1 = new Foo;
    auto f2 = new Foo;
    assert(f1 == f2);
}

If some optimisations are missed by structuring the methods in this way, then maybe that's something the compiler should be programmed to handle.

Reply via email to