Jump to page: 1 24  
Page
Thread overview
[Issue 13179] AA key type TagIndex now requires equality rather than comparison
Jul 21, 2014
Jacob Carlborg
Jul 21, 2014
Kenji Hara
Jul 22, 2014
Jacob Carlborg
Jul 23, 2014
Kenji Hara
Jul 23, 2014
Kenji Hara
Jul 23, 2014
Jonathan M Davis
Jul 23, 2014
Jonathan M Davis
Jul 23, 2014
Jonathan M Davis
Jul 23, 2014
Jonathan M Davis
Jul 24, 2014
Jonathan M Davis
Jul 24, 2014
Jacob Carlborg
Jul 24, 2014
Jacob Carlborg
Jul 25, 2014
Jonathan M Davis
Jul 25, 2014
Jacob Carlborg
Jul 25, 2014
Jonathan M Davis
Jul 25, 2014
Walter Bright
Jul 26, 2014
Walter Bright
Jul 26, 2014
Walter Bright
Jul 26, 2014
Walter Bright
Oct 13, 2014
Martin Nowak
Oct 19, 2014
Martin Nowak
Oct 19, 2014
Walter Bright
Oct 19, 2014
Martin Nowak
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

--- Comment #1 from Jacob Carlborg <doob@me.com> ---
Reduced test case:

struct TagIndex
{
    uint tag, index;

    const int opCmp(ref const TagIndex o)
    {
        if (tag == o.tag && index == o.index)
            return 0;
        if (tag > o.tag)
            return 1;
        if (tag < o.tag)
            return -1;
        if (index > o.index)
            return 1;
        if (index < o.index)
            return -1;
        assert(0);
    }
}

int[TagIndex] a;

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

hsteoh@quickfur.ath.cx changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |hsteoh@quickfur.ath.cx

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

--- Comment #2 from Kenji Hara <k.hara.pg@gmail.com> ---
(In reply to Jacob Carlborg from comment #0)
> Building Tango with 2.066.0-b5 results in the following error:
> 
> tango/text/Regex.d(2532): Error: AA key type TagIndex now requires equality
> rather than comparison
> tango/text/Regex.d(2532):        Please define opEquals, or remove opCmp to
> also rely on default memberwise comparison.
> 
> I can see that opEquals should be used and not opCmp for AA keys, but if opCmp is defined, why doesn't the compiler automatically generate opEquals that calls opCmp?

It's intended behavior, because compiler cannot know whether the generated opEquals is what programmer is expecting.

(In reply to Jacob Carlborg from comment #1)
> Reduced test case:
> 
> struct TagIndex
> {
>     uint tag, index;
> 
>     const int opCmp(ref const TagIndex o)
>     {
[snip]
>     }
> }
> 
> int[TagIndex] a;

In D, all struct define default member-wise equality for == operator.

TagIndex t1, t2;
// t1 == t2 will be rewritten as:
//    t1.tupleof == t2.tupleof
// and then it will mean:
//    t1.tag == t2.tag && t1.index == t2.index

If TagIndex is used for AA keys, compiler cannot determine which is the intended 'equality', t1 == t2 or t1.opCmp(t2) == 0.

So compiler requests you to resolve the ambiguity by defining opEquals or removing opCmp.

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

bearophile_hugs@eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bearophile_hugs@eml.cc

--- Comment #3 from bearophile_hugs@eml.cc ---
(In reply to Kenji Hara from comment #2)

> If TagIndex is used for AA keys, compiler cannot determine which is the intended 'equality', t1 == t2 or t1.opCmp(t2) == 0.

opCmp includes an equality, this means the programmer has defined an equality too. So can't we assume that the equality is "t1.opCmp(t2) == 0" when the user defines just opCmp?

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

--- Comment #4 from hsteoh@quickfur.ath.cx ---
(In reply to Kenji Hara from comment #2)
[...]
> It's intended behavior, because compiler cannot know whether the generated opEquals is what programmer is expecting.
[...]

That doesn't make sense. The programmer has already defined opCmp, which means "a == b" is already compiled to be "a.opCmp(b)==0". Therefore, if the programmer didn't define opEquals, it should simply default to:
----
bool opEquals(T b) { return a.opCmp(b)==0; }
----

By your logic, the compiler should reject "a == b" when the programmer hasn't defined opEquals, because the compiler cannot know whether a.opCmp(b)==0 is what the programmer is expecting. I don't understand how this could make any sense.

--
July 22, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

--- Comment #5 from Jacob Carlborg <doob@me.com> ---
(In reply to Kenji Hara from comment #2)

> It's intended behavior, because compiler cannot know whether the generated opEquals is what programmer is expecting.

As others have already said, if opCmp is defined then the compiler can generate opEquals that calls opCmp.

> In D, all struct define default member-wise equality for == operator.
> 
> TagIndex t1, t2;
> // t1 == t2 will be rewritten as:
> //    t1.tupleof == t2.tupleof
> // and then it will mean:
> //    t1.tag == t2.tag && t1.index == t2.index
> 
> If TagIndex is used for AA keys, compiler cannot determine which is the intended 'equality', t1 == t2 or t1.opCmp(t2) == 0.
> 
> So compiler requests you to resolve the ambiguity by defining opEquals or removing opCmp.

opCmp was added because of a regression in 2.065.0 and now it's changed again.

--
July 23, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

--- Comment #6 from Kenji Hara <k.hara.pg@gmail.com> ---
(In reply to Jacob Carlborg from comment #5)
> opCmp was added because of a regression in 2.065.0 and now it's changed again.

So you can just remove opCmp completely. For the struct

struct TagIndex
{
    uint tag, index;
}

In 2.066 AA will use default member-wise equality and hasing.

--
July 23, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

Kenji Hara <k.hara.pg@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|---                         |INVALID

--- Comment #7 from Kenji Hara <k.hara.pg@gmail.com> ---
(In reply to bearophile_hugs from comment #3)
> opCmp includes an equality, this means the programmer has defined an equality too. So can't we assume that the equality is "t1.opCmp(t2) == 0" when the user defines just opCmp?

But currently `a == b` is never rewritten to `a.opCmp(b) == 0`. It's in a domain of enhancement request that requires more discussion.

(In reply to hsteoh from comment #4)
> By your logic, the compiler should reject "a == b" when the programmer hasn't defined opEquals, because the compiler cannot know whether a.opCmp(b)==0 is what the programmer is expecting. I don't understand how this could make any sense.

It's not a logic issue. If opCmp is defined and `a == b` will be automatically rewritten to `a.opCmp(b) == 0`, the code behavior will be _silently_ changed.

So compiler reports a diagnostic error. It's far better than silent changing.

--
July 23, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

hsteoh@quickfur.ath.cx changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |---

--- Comment #8 from hsteoh@quickfur.ath.cx ---
(In reply to Kenji Hara from comment #7)
[...]
> But currently `a == b` is never rewritten to `a.opCmp(b) == 0`. It's in a domain of enhancement request that requires more discussion.

Wait, wat?? That's ridiculous... I think this is a bug. If I define opCmp, then a==b should work even if I don't define opEquals. Otherwise this leads to the current ridiculous situation:
------
struct S {
        int x;
        int y;
        int opCmp(S s) {
                return x - s.x;
        }
}

void main() {
        auto s1 = S(1,2);
        auto s2 = S(1,3);
        auto s3 = S(2,1);

        assert(s1 < s3); // OK
        assert(s2 < s3); // OK
        assert(s3 > s1); // OK
        assert(s3 > s2); // OK
        assert(s1 <= s2 && s2 >= s1); // OK
        assert(s1 == s2); // FAIL
}
------

The last two lines show that s1<=s2 && s1>=s2 does not imply s1==s2. I find that difficult to accept. I think this needs to be fixed.


> (In reply to hsteoh from comment #4)
> > By your logic, the compiler should reject "a == b" when the programmer hasn't defined opEquals, because the compiler cannot know whether a.opCmp(b)==0 is what the programmer is expecting. I don't understand how this could make any sense.
> 
> It's not a logic issue. If opCmp is defined and `a == b` will be automatically rewritten to `a.opCmp(b) == 0`, the code behavior will be _silently_ changed.
> 
> So compiler reports a diagnostic error. It's far better than silent changing.

I think that any code depends on the fact that s1<=s2 && s1>=s2 doesn't imply s1==s2, is already horribly broken.

--
July 23, 2014
https://issues.dlang.org/show_bug.cgi?id=13179

Jonathan M Davis <jmdavisProg@gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jmdavisProg@gmx.com

--- Comment #9 from Jonathan M Davis <jmdavisProg@gmx.com> ---
I would not expect `a == b` to _ever_ be converted to `a.opCmp(b) == 0`. That's what opEquals is for. However, I also wouldn't expect declaring opCmp to make it so that the compiler no longer generates an opEquals for the struct. The compiler should either continue to declare the opEquals or make it an error if you define opCmp but not opEquals. It makes no sense whatsoever to have a type which is comparable with <, <=, >=, and > but not ==. And it should _always_ be considered a bug if `a.opCmp(b) == 0` is not equivalent to `a == b` or if `a <= b && a >= b` is not equivalent to `a == b`.

--
« First   ‹ Prev
1 2 3 4