Thread overview
Qualified class opEquals()
Dec 24, 2018
Per Nordlöw
Dec 25, 2018
Paul Backus
Dec 25, 2018
Per Nordlöw
Dec 25, 2018
Adam D. Ruppe
Dec 27, 2018
Jonathan M Davis
December 24, 2018
Is it in the following code possible to make the statement

    assert(car1 == car2);

in the function testEqual() compile in a `@safe pure nothrow @nogc context`?

Code:


import core.internal.hash : hashOf;

/** Hash that distinguishes `Expr(X)` from `NounExpr(X)`.
 *
 * See_Also: https://forum.dlang.org/post/lxqoknwuujbymolnlyfw@forum.dlang.org
 */
hash_t hashOfPolymorphic(Class)(Class aClassInstance) @trusted pure nothrow @nogc
if (is(Class == class))
{
    assert(Class.alignof == 8);
    return (cast(hash_t)(cast(void*)typeid(Class)) >> 3) ^ hashOf(aClassInstance);
}

version(unittest)
{
    private static:

    class Thing
    {
        @property override bool opEquals(const scope Object that) const @safe pure nothrow @nogc
        {
            if (typeid(this) !is typeid(that)) { return false; }
            assert(0);
        }
        @property final bool opEquals(const scope typeof(this) that) const @safe pure nothrow @nogc
        {
            assert(0);
        }
    }

    class Expr : Thing
    {
        @safe pure nothrow @nogc:
        alias Data = string;
        this(Data data)
        {
            this.data = data;
        }
        @property override bool opEquals(const scope Object that) const @safe pure nothrow @nogc
        {
            if (typeid(this) !is typeid(that)) { return false; }
            return data == (cast(typeof(this))(that)).data;
        }
        @property final bool opEquals(const scope typeof(this) that) const @safe pure nothrow @nogc
        {
            if (typeid(this) !is typeid(that)) { return false; }
            return data == (cast(typeof(this))(that)).data;
        }
        @property override hash_t toHash() const @safe pure nothrow @nogc
        {
            return hashOf(data);
        }
        Data data;
    }

    class NounExpr : Expr
    {
        @safe pure nothrow @nogc:
        this(Data data)
        {
            super(data);
        }
        @property override hash_t toHash() const @safe pure nothrow @nogc
        {
            return hashOf(data);
        }
    }

    class Year : Thing
    {
        @safe pure nothrow @nogc:
        alias Data = long;
        @property override hash_t toHash() const @safe pure nothrow @nogc
        {
            return hashOf(data);
        }
        Data data;
    }
}

@safe pure nothrow unittest
{
    auto car1 = new Expr("car");
    auto car2 = new Expr("car");
    auto bar1 = new Expr("bar");
    auto ncar = new NounExpr("car");

    void testEqual() @safe pure nothrow @nogc
    {
        assert(car1.opEquals(car2));
        assert(!car1.opEquals(bar1));
        assert(!car2.opEquals(bar1));
        // TODO should compile: assert(car1 == car2);
        assert(hashOf(car1) == hashOf(car2));
        assert(hashOfPolymorphic(car1) == hashOfPolymorphic(car2));
    }

    void testDifferent1() @safe pure nothrow @nogc
    {
        assert(!car1.opEquals(bar1));
        // TODO should compile: assert(car1 != bar1);
        assert(hashOf(car1) != hashOf(bar1));
        assert(hashOfPolymorphic(car1) != hashOfPolymorphic(bar1));
    }

    void testDifferent2() @safe pure nothrow @nogc
    {
        assert(hashOf(car1) == hashOf(ncar));
        assert(hashOfPolymorphic(car1) != hashOfPolymorphic(ncar));
    }

    testEqual();
    testDifferent1();
    testDifferent2();
}

December 25, 2018
On Monday, 24 December 2018 at 22:58:03 UTC, Per Nordlöw wrote:
> Is it in the following code possible to make the statement
>
>     assert(car1 == car2);
>
> in the function testEqual() compile in a `@safe pure nothrow @nogc context`?

No, because equality comparison between classes lowers to `object.opEquals` [1], which takes both parameters as `Object`.

If you call car1.opEquals(car2) directly, it should work.

[1] https://github.com/dlang/druntime/blob/master/src/object.d#L1051
December 25, 2018
On Tuesday, 25 December 2018 at 00:32:55 UTC, Paul Backus wrote:
> No, because equality comparison between classes lowers to `object.opEquals` [1], which takes both parameters as `Object`.

This is a severe limitation. Are there any plans on fixing this or do I have to wait for Andrei's proposed ProtoObject?
December 25, 2018
On Tuesday, 25 December 2018 at 14:27:39 UTC, Per Nordlöw wrote:
> This is a severe limitation. Are there any plans on fixing this or do I have to wait for Andrei's proposed ProtoObject?

Ignore opEquals and use your own interface.
December 27, 2018
On Tuesday, December 25, 2018 7:27:39 AM MST Per Nordlöw via Digitalmars-d- learn wrote:
> On Tuesday, 25 December 2018 at 00:32:55 UTC, Paul Backus wrote:
> > No, because equality comparison between classes lowers to `object.opEquals` [1], which takes both parameters as `Object`.
>
> This is a severe limitation. Are there any plans on fixing this or do I have to wait for Andrei's proposed ProtoObject?

ProtoObject _is_ the proposed fix, but if you want to use Object's opEquals in @safe code, you can always create a wrapper function that's marked @trusted and call that instead.

- Jonathan M Davis