diff --git a/src/object.di b/src/object.di
index 35995b8df1..0d817fac49 100644
--- a/src/object.di
+++ b/src/object.di
@@ -1,7 +1,7 @@
/**
* Contains all implicitly declared types and variables.
*
- * Copyright: Copyright Digital Mars 2000 - 2011.
+ * Copyright: Copyright Digital Mars 2000 - 2014.
* License: Boost License 1.0.
* Authors: Walter Bright, Sean Kelly
*
@@ -45,8 +45,42 @@ class Object
static Object factory(string classname);
}
-bool opEquals(const Object lhs, const Object rhs);
-bool opEquals(Object lhs, Object rhs);
+bool opEquals(T, U)(T lhs, U rhs)
+ if (is(T == class) && is(U == class) &&
+ is(typeof(lhs.opEquals(rhs)) == bool) &&
+ is(typeof(rhs.opEquals(lhs)) == bool))
+{
+ static if (is(T : U) || is(U : T))
+ {
+ // If aliased to the same object or both null => equal
+ if (lhs is rhs) return true;
+ }
+
+ // If either is null => non-equal
+ if (lhs is null || rhs is null) return false;
+
+ // If same exact type => one call to method opEquals
+ // General case => symmetric calls to method opEquals
+ return lhs.opEquals(rhs) &&
+ (typeid(lhs) is typeid(lhs) || typeid(lhs).opEquals(typeid(rhs)) || rhs.opEquals(lhs));
+}
+
+bool opEquals(T, U)(const T lhs, const U rhs)
+ if (is(T == class) && is(U == class) &&
+ !is(typeof(lhs.opEquals(rhs)) == bool) &&
+ !is(typeof(rhs.opEquals(lhs)) == bool))
+{
+ // FIXME. This is a hack.
+ // We shouldn't need to cast away const, and if either lhs' or rhs' opEquals
+ // mutates either object, it's undefined behavior. But before we can remove
+ // this, we need to make it so that TypeInfo and friends have the corect
+ // definitions for opEquals so that they work with the other overload. And
+ // any user code using const objects but which doesn't define opEquals such
+ // that it works with const with the other overload will also break once
+ // this is removed. So, we need to get rid of this, but we need to be
+ // careful about how and when we do it.
+ return opEquals(cast()lhs, cast()rhs);
+}
void setSameMutex(shared Object ownee, shared Object owner);
diff --git a/src/object_.d b/src/object_.d
index 7231367416..04d5c0a82f 100644
--- a/src/object_.d
+++ b/src/object_.d
@@ -5,12 +5,12 @@
* Macros:
* WIKI = Object
*
- * Copyright: Copyright Digital Mars 2000 - 2011.
+ * Copyright: Copyright Digital Mars 2000 - 2014.
* License: Boost License 1.0.
* Authors: Walter Bright, Sean Kelly
*/
-/* Copyright Digital Mars 2000 - 2011.
+/* Copyright Digital Mars 2000 - 2014.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -143,28 +143,219 @@ class Object
/************************
* Returns true if lhs and rhs are equal.
*/
-bool opEquals(const Object lhs, const Object rhs)
+bool opEquals(T, U)(T lhs, U rhs)
+ if (is(T == class) && is(U == class) &&
+ is(typeof(lhs.opEquals(rhs)) == bool) &&
+ is(typeof(rhs.opEquals(lhs)) == bool))
{
- // A hack for the moment.
+ static if (is(T : U) || is(U : T))
+ {
+ // If aliased to the same object or both null => equal
+ if (lhs is rhs) return true;
+ }
+
+ // If either is null => non-equal
+ if (lhs is null || rhs is null) return false;
+
+ // If same exact type => one call to method opEquals
+ // General case => symmetric calls to method opEquals
+ return lhs.opEquals(rhs) &&
+ (typeid(lhs) is typeid(lhs) || typeid(lhs).opEquals(typeid(rhs)) || rhs.opEquals(lhs));
+}
+
+bool opEquals(T, U)(const T lhs, const U rhs)
+ if (is(T == class) && is(U == class) &&
+ !is(typeof(lhs.opEquals(rhs)) == bool) &&
+ !is(typeof(rhs.opEquals(lhs)) == bool))
+{
+ // FIXME. This is a hack.
+ // We shouldn't need to cast away const, and if either lhs' or rhs' opEquals
+ // mutates either object, it's undefined behavior. But before we can remove
+ // this, we need to make it so that TypeInfo and friends have the corect
+ // definitions for opEquals so that they work with the other overload. And
+ // any user code using const objects but which doesn't define opEquals such
+ // that it works with const with the other overload will also break once
+ // this is removed. So, we need to get rid of this, but we need to be
+ // careful about how and when we do it.
return opEquals(cast()lhs, cast()rhs);
}
-bool opEquals(Object lhs, Object rhs)
+private void _testOpEquals(T, U)(bool expected, T lhs, U rhs, size_t line = __LINE__)
{
- // If aliased to the same object or both null => equal
- if (lhs is rhs) return true;
+ import core.exception;
+ enum typesStr = T.stringof ~ ", " ~ U.stringof;
- // If either is null => non-equal
- if (lhs is null || rhs is null) return false;
+ if ((lhs == rhs) != expected)
+ throw new AssertError("unittest failure: mutable vs mutable: " ~ typesStr, __FILE__, line);
+ if ((lhs == cast(const U)rhs) != expected)
+ throw new AssertError("unittest failure: mutable vs const: " ~ typesStr, __FILE__, line);
+ if ((lhs == cast(immutable U)rhs) != expected)
+ throw new AssertError("unittest failure: mutable vs immutable: " ~ typesStr, __FILE__, line);
+ if (((cast(const T)lhs) == rhs) != expected)
+ throw new AssertError("unittest failure: const vs mutable: " ~ typesStr, __FILE__, line);
+ if (((cast(const T)lhs) == cast(const U)rhs) != expected)
+ throw new AssertError("unittest failure: const vs const: " ~ typesStr, __FILE__, line);
+ if (((cast(const T)lhs) == cast(immutable U)rhs) != expected)
+ throw new AssertError("unittest failure: const vs immutable: " ~ typesStr, __FILE__, line);
+ if (((cast(immutable T)lhs) == rhs) != expected)
+ throw new AssertError("unittest failure: immutable vs mutable: " ~ typesStr, __FILE__, line);
+ if (((cast(immutable T)lhs) == cast(const U)rhs) != expected)
+ throw new AssertError("unittest failure: immutable vs const: " ~ typesStr, __FILE__, line);
+ if (((cast(immutable T)lhs) == cast(immutable U)rhs) != expected)
+ throw new AssertError("unittest failure: immutable vs immutable: " ~ typesStr, __FILE__, line);
+}
- // If same exact type => one call to method opEquals
- if (typeid(lhs) is typeid(rhs) || typeid(lhs).opEquals(typeid(rhs)))
- return lhs.opEquals(rhs);
+private void _testOpEquals2(T, U, V)(bool expected, V lhs, V rhs, size_t line = __LINE__)
+{
+ _testOpEquals!(T, T)(expected, lhs, rhs, line);
+ _testOpEquals!(T, U)(expected, lhs, rhs, line);
+ _testOpEquals!(U, T)(expected, lhs, rhs, line);
+ _testOpEquals!(U, U)(expected, lhs, rhs, line);
+}
- // General case => symmetric calls to method opEquals
- return lhs.opEquals(rhs) && rhs.opEquals(lhs);
+unittest
+{
+ static class C
+ {
+ bool opEquals(const C rhs) const { return _i == rhs._i; }
+ this(int i) { _i = i; }
+ int _i;
+ }
+
+ static class D : C
+ {
+ override bool opEquals(const C rhs) const
+ {
+ if (auto dRHS = cast(D)rhs)
+ return this.opEquals(dRHS);
+ return super.opEquals(rhs);
+ }
+
+ bool opEquals(const D rhs) const { return super.opEquals(rhs) && _s == rhs._s; }
+ this(string s, int i) { super(i); _s = s; }
+ string _s;
+ }
+
+ static class E : C
+ {
+ override bool opEquals(const C rhs) const
+ {
+ if (auto dRHS = cast(E)rhs)
+ return this.opEquals(dRHS);
+ return super.opEquals(rhs);
+ }
+
+ bool opEquals(const E rhs) const { return super.opEquals(rhs) && _b == rhs._b; }
+ this(bool b, int i) { super(i); _b = b; }
+ bool _b;
+ }
+
+ auto c42 = new C(42);
+ auto c120 = new C(120);
+ auto c42Dup = new C(42);
+
+ assert(c42 == c42);
+ assert(c42 != c120);
+ assert(c42 == c42Dup);
+ assert(c120 != c42);
+ assert(c120 == c120);
+ assert(c120 != c42Dup);
+ assert(c42Dup == c42);
+ assert(c42Dup != c120);
+ assert(c42Dup == c42Dup);
+
+ auto dFoo42 = new D("foo", 42);
+ auto dBar42 = new D("bar", 42);
+ auto dFoo120 = new D("foo", 120);
+ auto dBar120 = new D("bar", 120);
+ auto dFoo42Dup = new D("foo", 42);
+
+ _testOpEquals2!(C, D)(true, dFoo42, dFoo42);
+ _testOpEquals2!(C, D)(false, dFoo42, dBar42);
+ _testOpEquals2!(C, D)(false, dFoo42, dFoo120);
+ _testOpEquals2!(C, D)(false, dFoo42, dBar120);
+ _testOpEquals2!(C, D)(true, dFoo42, dFoo42Dup);
+ _testOpEquals2!(C, D)(false, dBar42, dFoo42);
+ _testOpEquals2!(C, D)(true, dBar42, dBar42);
+ _testOpEquals2!(C, D)(false, dBar42, dFoo120);
+ _testOpEquals2!(C, D)(false, dBar42, dBar120);
+ _testOpEquals2!(C, D)(false, dBar42, dFoo42Dup);
+ _testOpEquals2!(C, D)(false, dFoo120, dFoo42);
+ _testOpEquals2!(C, D)(false, dFoo120, dBar42);
+ _testOpEquals2!(C, D)(true, dFoo120, dFoo120);
+ _testOpEquals2!(C, D)(false, dFoo120, dBar120);
+ _testOpEquals2!(C, D)(false, dFoo120, dFoo42Dup);
+ _testOpEquals2!(C, D)(false, dBar120, dFoo42);
+ _testOpEquals2!(C, D)(false, dBar120, dBar42);
+ _testOpEquals2!(C, D)(false, dBar120, dFoo120);
+ _testOpEquals2!(C, D)(true, dBar120, dBar120);
+ _testOpEquals2!(C, D)(false, dBar120, dFoo42Dup);
+ _testOpEquals2!(C, D)(true, dFoo42Dup, dFoo42);
+ _testOpEquals2!(C, D)(false, dFoo42Dup, dBar42);
+ _testOpEquals2!(C, D)(false, dFoo42Dup, dFoo120);
+ _testOpEquals2!(C, D)(false, dFoo42Dup, dBar120);
+ _testOpEquals2!(C, D)(true, dFoo42Dup, dFoo42Dup);
+
+ _testOpEquals(true, c42, dFoo42);
+ _testOpEquals(true, c42, dBar42);
+ _testOpEquals(false, c42, dFoo120);
+ _testOpEquals(false, c42, dBar120);
+ _testOpEquals(false, c120, dFoo42);
+ _testOpEquals(false, c120, dBar42);
+ _testOpEquals(true, c120, dFoo120);
+ _testOpEquals(true, c120, dBar120);
+ _testOpEquals(true, dFoo42, c42);
+ _testOpEquals(true, dBar42, c42);
+ _testOpEquals(false, dFoo120, c42);
+ _testOpEquals(false, dBar120, c42);
+ _testOpEquals(false, dFoo42, c120);
+ _testOpEquals(false, dBar42, c120);
+ _testOpEquals(true, dFoo120, c120);
+ _testOpEquals(true, dBar120, c120);
+
+ auto eTrue42 = new E(true, 42);
+ auto eFalse42 = new E(false, 42);
+ auto eTrue120 = new E(true, 120);
+ auto eFalse120 = new E(false, 120);
+ auto eTrue42Dup = new E(true, 42);
+
+ _testOpEquals2!(C, E)(true, eTrue42, eTrue42);
+ _testOpEquals2!(C, E)(false, eTrue42, eFalse42);
+ _testOpEquals2!(C, E)(false, eTrue42, eTrue120);
+ _testOpEquals2!(C, E)(false, eTrue42, eFalse120);
+ _testOpEquals2!(C, E)(true, eTrue42, eTrue42Dup);
+ _testOpEquals2!(C, E)(false, eFalse42, eTrue42);
+ _testOpEquals2!(C, E)(true, eFalse42, eFalse42);
+ _testOpEquals2!(C, E)(false, eFalse42, eTrue120);
+ _testOpEquals2!(C, E)(false, eFalse42, eFalse120);
+ _testOpEquals2!(C, E)(false, eFalse42, eTrue42Dup);
+ _testOpEquals2!(C, E)(false, eTrue120, eTrue42);
+ _testOpEquals2!(C, E)(false, eTrue120, eFalse42);
+ _testOpEquals2!(C, E)(true, eTrue120, eTrue120);
+ _testOpEquals2!(C, E)(false, eTrue120, eFalse120);
+ _testOpEquals2!(C, E)(false, eTrue120, eTrue42Dup);
+ _testOpEquals2!(C, E)(false, eFalse120, eTrue42);
+ _testOpEquals2!(C, E)(false, eFalse120, eFalse42);
+ _testOpEquals2!(C, E)(false, eFalse120, eTrue120);
+ _testOpEquals2!(C, E)(true, eFalse120, eFalse120);
+ _testOpEquals2!(C, E)(false, eFalse120, eTrue42Dup);
+ _testOpEquals2!(C, E)(true, eTrue42Dup, eTrue42);
+ _testOpEquals2!(C, E)(false, eTrue42Dup, eFalse42);
+ _testOpEquals2!(C, E)(false, eTrue42Dup, eTrue120);
+ _testOpEquals2!(C, E)(false, eTrue42Dup, eFalse120);
+ _testOpEquals2!(C, E)(true, eTrue42Dup, eTrue42Dup);
+
+ _testOpEquals(true, eTrue42, dFoo42);
+ _testOpEquals(true, eTrue42, dBar42);
+ _testOpEquals(false, eTrue42, dFoo120);
+ _testOpEquals(false, eTrue42, dBar120);
+ _testOpEquals(false, eFalse120, dFoo42);
+ _testOpEquals(false, eFalse120, dBar42);
+ _testOpEquals(true, eFalse120, dFoo120);
+ _testOpEquals(true, eFalse120, dBar120);
}
+
/**
* Information about an interface.
* When an object is accessed via an interface, an Interface* appears as the