diff --git a/std/typecons.d b/std/typecons.d index 6f4fa35bc08..8d0b9bf55b2 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -2128,6 +2128,18 @@ if (is(T == class) || is(T == interface) || isAssociativeArray!T) } } + bool opEquals()(auto ref const(typeof(this)) rhs) const + { + // Must forward explicitly because 'stripped' is part of a union. + // The necessary 'toHash' is forwarded to the class via alias this. + return stripped == rhs.stripped; + } + + bool opEquals(const(U) rhs) const + { + return stripped == rhs; + } + alias get this; } @@ -2206,6 +2218,61 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr static assert(!__traits(compiles, &r.get())); } +@safe unittest +{ + class CustomToHash + { + override size_t toHash() const nothrow @trusted { return 42; } + } + Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash(); + assert(a.toHash() == 42, "Rebindable!A should offer toHash()" + ~ " by forwarding to A.toHash()."); +} + +@system unittest // issue 18615: Rebindable!A should use A.opEquals +{ + class CustomOpEq + { + int x; + override bool opEquals(Object rhsObj) + { + if (auto rhs = cast(const(CustomOpEq)) rhsObj) + return this.x == rhs.x; + else + return false; + } + } + CustomOpEq a = new CustomOpEq(); + CustomOpEq b = new CustomOpEq(); + assert(a !is b); + assert(a == b, "a.x == b.x should be true (0 == 0)."); + + Rebindable!(const(CustomOpEq)) ra = a; + Rebindable!(const(CustomOpEq)) rb = b; + assert(ra !is rb); + assert(ra == rb, "Rebindable should use CustomOpEq's opEquals, not 'is'."); + assert(ra == b, "Rebindable!(someQualifier(A)) should be comparable" + ~ " against const(A) via A.opEquals."); + assert(a == rb, "Rebindable!(someQualifier(A)) should be comparable" + ~ " against const(A) via A.opEquals."); + + b.x = 1; + assert(a != b); + assert(ra != b, "Rebindable!(someQualifier(A)) should be comparable" + ~ " against const(A) via A.opEquals."); + assert(a != rb, "Rebindable!(someQualifier(A)) should be comparable" + ~ " against const(A) via A.opEquals."); + + Rebindable!(const(Object)) o1 = new Object(); + Rebindable!(const(Object)) o2 = new Object(); + assert(o1 !is o2); + assert(o1 == o1, "When the class doesn't provide its own opEquals," + ~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals."); + assert(o1 != o2, "When the class doesn't provide its own opEquals," + ~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals."); + assert(o1 != new Object(), "Rebindable!(const(Object)) should be" + ~ " comparable against Object itself and use Object.opEquals."); +} /** Convenience function for creating a `Rebindable` using automatic type inference.