Wrong selection of opEquals for objects.

Simen Kjærås simen.kjaras at gmail.com
Fri Aug 28 10:28:07 UTC 2020


On Friday, 28 August 2020 at 08:16:01 UTC, Alexandru Ermicioi 
wrote:
> Hi everyone,
>
> there is https://issues.dlang.org/show_bug.cgi?id=21180 bug, 
> anyone knows how to avoid it?
>
> Test case:
> -------------
> import std;
>
> class Silly {
>     bool opEquals(const Silly silly) const @safe {
>         return silly is this;
>     }
>
>     alias opEquals = Object.opEquals;
> }
>
> bool comp(T)() @safe {
>     return new T() == new T();
> }
>
> void main()
> {
>     comp!Silly.writeln;
>     comp!(const Silly).writeln;
>     comp!(immutable Silly).writeln;
> }
> -------------
>
> It always tries to call Object.opEquals, when narrower overload 
> should've been selected.
>
> - Alex.

Essentially, this boils down to the issues described in 
https://issues.dlang.org/show_bug.cgi?id=1824, and a host of 
other bugzilla issues.

When you do a == b with a and b being class objects, it's lowered 
to object.opEquals(a, b)[0], which casts both a and b to Object 
before doing the comparison. So, you'll need to override 
Object.opEquals to have the right function called:

class Silly {
     int field;
     this(int f) {
         field = f;
     }
     override bool opEquals(Object o) {
         auto silly = cast(Silly)o;

         // If cast returns null, it's not a Silly instance
         if (!silly) return false;

         // Compare Silly objects
         return field == silly.field;
     }
}

unittest {
     Silly a = new Silly(1);
     assert(a == new Silly(1));
     assert(a != new Silly(2));
}

That takes care of choosing the correct overload, but as you may 
have noticed, there's another issue: your @safe function comp() 
can't call the @system function object.opEquals. Since 
object.opEquals operates on Object instances, not your specific 
subclass, it has to assume the worst, and is @system. There's no 
real good solution to that in the language as of now, and some of 
us have been pulling our hair for years because of it.

What you'll need to do is mark every function that does compare 
two class objects with == as @trusted or @system.

--
   Simen


[0]: 
https://github.com/dlang/druntime/blob/master/src/object.d#L166


More information about the Digitalmars-d-learn mailing list