原文
以下代码编译:
class C { int a; this(int) @safe {} }void main()
{C c = new C(1);C[] a = [c, c, c];assert(a == [c, c, c]);
}
以下代码失败:
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]);
}
C[]和C[3]不匹配.
这像是2.078.1的回归.显然,对象(object).__equals!(C,C).__equals函数不是@safe.但是,从2.094.1开始,此错误已更改为:错误:'C[]'和'C[3]'的数组比较类型不兼容.
我想要修复需要两部分:1,更改dmd以避免错误地显示不兼容类型的错误;2更改druntime,使比较安全.
这不是编译器错误.而是druntime错误[1],可见构造了druntime调用,然后试了语义.
如果有错误,编译器会简单地假设类型不兼容.这是kinke在[2]中引入的,这样用户得不到指向druntime的错误.
如果在[2]中使用"expressionSemantic"而不是"trySemantic",则会得到错误说:
'core.object.opEquals'[3]不是@safe,并且因为它调用了'Object.opEquals'不能是@safe的.
事实上,仅比较两个用户未定义opEquals的类,'assert(c==c)'就会在@safe代码中发出错误:"Dmain"这个"@safe"函数无法调用opEquals"这个"@system"函数对象.
因此,根本问题是'object.opEquals'是不安全的,并且不能使其成为@safe,因为这样,类就只能限制为@safe的opEquals.(是否可更改属性继承规则来绕过该问题[4]中的第6点)
1
2
3
4
刚在2.102.1中遇见了该问题.示例中,错误消息更令人吃惊:
"X[]"和"X[]"数组比较不兼容类型
这已帮助我猜测原因是@systemObject.opEquals.
但是整个问题,不论是'C[3]'还是'X[]',都可能让新手困惑.他们遵循@safe的最佳实践,照本宣科地比较数组,结果却遇见了错误,即甚至不能让数组与其自身比较.
考虑以下:
class C { int a; this(int) @safe {} }
class D : C {this(int a) @safe { super(a); }override bool opEquals(Object rhs) const {// 明显不安全.*(cast(int*) 0x5afe) = 0xdead5afe;return true;}
}@safe void main()
{C c = new D(1);C[] a = [c, c, c];assert(a == [c, c, c]);
}
编译器错误消息应该告诉,用'@safe'覆盖子类中的opEquals,尽管这样做仍会发出错误.
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]);
}
所以应该允许后者,错误消息应该在禁止前者时,告诉你应该编写它.
如果比较的类的类型不覆盖opEquals等于操作,则最后调用Object.opEquals.
因为Object.opEquals是@系统,@安全用户代码会发出令人困惑的比较不兼容类型.
为此,只需要检查是否正在调用Object.opEquals,并且信任它(只是在是比较).
不让Object.opEquals为@安全?因为会破坏大量代码.
在我看来,最初的bug是一个诊断bug,编译器没有给出有用错误消息,但行为是正确的.是系统就是这样不应从安全代码调用.