Comment #0 by lucidanescu28 — 2021-07-30T12:03:20Z
The following code compiles:
class C { int a; this(int) @safe {} }
void main()
{
C c = new C(1);
C[] a = [c, c, c];
assert(a == [c, c, c]);
}
The following code fails:
class C { int a; this(int) @safe {} }
@safe void main()
{
C c = new C(1);
C[] a = [c, c, c];
assert(a == [c, c, c]);
}
with the message: incompatible types for array comparison: `C[]` and `C[3]`
Comment #1 by razvan.nitu1305 — 2021-07-30T12:42:11Z
Looks like this is a regression from 2.078.1 . Apparently, the object.__equals!(C, C).__equals function was not @safe. However, since 2.094.1 this error has changed to: Error: incompatible types for array comparison: `C[]` and `C[3]`. I suspect that the fix will require 2 parts: 1 change dmd to not wrongfully error about incompatible types; 1 change to druntime to make the comparison safe.
Comment #2 by razvan.nitu1305 — 2021-07-30T13:11:09Z
It turns out this is a druntime bug.
Comment #3 by razvan.nitu1305 — 2021-08-04T07:03:57Z
It's not a compiler bug. Here is where the error is emitted [1]. You can see that the druntime call is constructed and then semantic is tried.
If there are any errors, the compiler simply assumes that the types are not compatible. This was introduced by kinke in [2] so that the user
will not get errors pointing to druntime code. If in [2] we use `expressionSemantic` instead of `trySemantic` we would get an error stating
that `core.object.opEquals` [3] is not @safe and cannot be @safe because it calls `Object.opEquals`. In fact, just comparing 2 classes with no user-defined opEquals - `assert (c == c)` - will issue an error in @safe code: "`@safe` function `D main` cannot call `@system` function `object.opEquals`".
Therefore, the underlying issue is that `object.opEquals` is unsafe and cannot be made @safe because then any class is constrained
to have a @safe opEquals. (I wonder if we can get around this by changing the attribute inheritance rule - point 6 in [4])
[1] https://github.com/dlang/dmd/blob/master/src/dmd/expressionsem.d#L11416
[2] https://github.com/dlang/dmd/pull/11212/files#diff-f0a120d764c7fab59bd3210b19fe685d2f106e4a1b9a33266804ae99dd3bbd4eR11135
[3] https://github.com/dlang/druntime/blob/master/src/object.d#L240
[4] https://dlang.org/spec/function.html#function-inheritance
Comment #4 by s.naarmann — 2023-03-15T05:49:38Z
I just ran into this in 2.102.1. In my case, the error message was even more eyebrow-raising:
incompatible types for array comparison: `X[]` and `X[]`
This already helped me guess that the culprit was the @system Object.opEquals.
But the entire issue, regardless of `C[3]` or `X[]`, has newbie-confusing potential. They follow best practices with @safe, compare arrays by the book, and run into the error that they can't compare an array even with itself.
Comment #5 by dlang-bot — 2023-03-15T13:40:02Z
@RazvanN7 created dlang/dmd pull request #14988 "Fix Issue 22159 - == causes error for array of classes in safe method" fixing this issue:
- Fix Issue 22159 - == causeses error for array of classes in safe method
https://github.com/dlang/dmd/pull/14988
Comment #6 by destructionator — 2023-03-15T17:59:44Z
Consider the following:
---
class C { int a; this(int) @safe {} }
class D : C {
this(int a) @safe { super(a); }
override bool opEquals(Object rhs) const {
// obviously not safe
*(cast(int*) 0x5afe) = 0xdead5afe;
return true;
}
}
@safe void main()
{
C c = new D(1);
C[] a = [c, c, c];
assert(a == [c, c, c]);
}
---
The compiler error message should tell you to make a `@safe` override for opEquals in your child class, though doing this still issues an error
---
class C {
int a; this(int) @safe {}
override bool opEquals(Object rhs) @safe {
return this is rhs;
}
}
@safe void main()
{
C c = new C(1);
C[] a = [c, c, c];
assert(a == [c, c, c]);
}
---
So it should really allow this latter thing and the error message should tell you to write it, while still prohibiting the earlier example.
Comment #7 by robert.schadek — 2024-12-07T13:41:12Z