Bug 22159 – "==" causeses error for array of classes in safe method

Status
NEW
Severity
regression
Priority
P1
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2021-07-30T12:03:20Z
Last change time
2024-12-07T13:41:12Z
Keywords
pull, safe
Assigned to
No Owner
Creator
Danescu Lucian
Moved to GitHub: dmd#17425 →

Comments

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
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/17425 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB