digitalmars.D.learn - opEquals safe is ignored
- Luis (34/34) May 24 2020 Lets take this example code (https://run.dlang.io/is/Vkpx9j) :
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (21/28) May 24 2020 Looking at the output, it's actually druntime's opEquals that
- Jonathan M Davis (33/67) May 24 2020 The core problem is that Object does not have attributes on any of its
Lets take this example code (https://run.dlang.io/is/Vkpx9j) : ´´´D import std; void main() { } class ExampleC { int x; this (int x) safe { this.x = x; } override bool opEquals(Object o) const trusted { if (ExampleC rhs = cast(ExampleC)o) { return this.x == rhs.x; } return false; } } safe unittest { auto c = new ExampleC(1); assert(c != new ExampleC(23)); } ´´´ dmd ignores trusted or safe on opEquals, throwing this error : onlineapp.d(27): Error: safe function onlineapp.__unittest_L24_C7 cannot call system function object.opEquals An override system or trusted function can't be safe, or I it a bug ? Also, how will this be affected by DIP1028 ?
May 24 2020
On Sunday, 24 May 2020 at 08:57:28 UTC, Luis wrote:dmd ignores trusted or safe on opEquals, throwing this error : onlineapp.d(27): Error: safe function onlineapp.__unittest_L24_C7 cannot call system function object.opEquals An override system or trusted function can't be safe, or I it a bug ? Also, how will this be affected by DIP1028 ?Looking at the output, it's actually druntime's opEquals that can't be called. That's the global function that ensure opEquals is called on both sides of the comparison, and it's not safe. This makes classes even worse to use in D than, and I'm not sure how to properly handle this. If we make it trusted, suddenly I can call system opEquals from safe functions without issue. Root cause is, classes in D all inherit from Object, which has some defaults that aren't always what you want. It seems DIP1028 will make your code automatically compile, along with this: class C { override bool opEquals(Object o) system { *cast(int*)123456 = 3; return true; } } safe void mySafeFunction() { assert(new C() == new C()); } -- Simen
May 24 2020
On Sunday, May 24, 2020 2:57:28 AM MDT Luis via Digitalmars-d-learn wrote:Lets take this example code (https://run.dlang.io/is/Vkpx9j) : ´´´D import std; void main() { } class ExampleC { int x; this (int x) safe { this.x = x; } override bool opEquals(Object o) const trusted { if (ExampleC rhs = cast(ExampleC)o) { return this.x == rhs.x; } return false; } } safe unittest { auto c = new ExampleC(1); assert(c != new ExampleC(23)); } ´´´ dmd ignores trusted or safe on opEquals, throwing this error : onlineapp.d(27): Error: safe function onlineapp.__unittest_L24_C7 cannot call system function object.opEquals An override system or trusted function can't be safe, or I it a bug ? Also, how will this be affected by DIP1028 ?The core problem is that Object does not have attributes on any of its functions - including opEquals. And in the case of opEquals, the problem is one layer worse, because == gets lowered to the free function opEquals which in turn calls opEquals on the class reference itself (after doing additional checks like that the reference isn't null and that you're not comparing a reference to itself), and that free function has no attributes. In fact, the only reason that using == on const objects works is because of a hack in druntime that casts away const (which means that it's technically possible and even downright trivial to break the type system by mutating a const class object within opEquals). And really, the only fix for this is to remove opEquals, opCmp, toHash, and toString from Object, since no matter which set of attributes you pick, there will be problems. With the addition of templates, it's no longer necessary for any of those functions to even be on Object, and it really doesn't make sense for them to be there, but they've been there since long before templates or attributes were added to the language. It was decided years ago that those functions should be removed from Object, but _how_ to do it with minimal code breakage is a thorny problem, and it hasn't really been a priority. The currently proposed solution (which has a work-in-progress DIP IIRC) is to introduce a new root class to the language below Object which has _nothing_ on it (the current name for that root class being ProtoObject). Object would then continue to be the default base class of any class, but it would then be derived from ProtoObject, and best practice at that point would really be to explicitly derive your class from ProtoObject (with Object being left in place pretty much just to avoid breaking existing code). However, the DIP for ProtoObject has yet to be fully sorted out, and it definitely hasn't been accepted yet, so it can't yet fix the problem. So, for now, you're basically forced to use trusted when comparing class references in safe code. It's annoying, but because Object's opEquals is system, we're kind of stuck at the moment. - Jonathan M Davis
May 24 2020