Skip to content

Commit

Permalink
Fix Issue 18615 - Rebindable!A calls A.opEquals
Browse files Browse the repository at this point in the history
The issue was that Rebindable!A always compared two rebindable
references by 'a is b', not by A.opEquals. With this fix, two rebindable
references, or a rebindable and a raw reference, always compare with
A.opEquals.
  • Loading branch information
SimonN authored and wilzbach committed Apr 5, 2018
1 parent 25edf91 commit 65b1f1a
Showing 1 changed file with 67 additions and 0 deletions.
67 changes: 67 additions & 0 deletions std/typecons.d
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 65b1f1a

Please sign in to comment.