digitalmars.D.bugs - [Issue 5519] New: Saner struct equality
- d-bugmail puremagic.com (98/98) Feb 02 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
- d-bugmail puremagic.com (35/73) Feb 03 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
- d-bugmail puremagic.com (9/24) Feb 03 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
- d-bugmail puremagic.com (12/12) Feb 03 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
- d-bugmail puremagic.com (33/33) Feb 03 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
- d-bugmail puremagic.com (6/6) Feb 04 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
- d-bugmail puremagic.com (12/12) Feb 04 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5519
http://d.puremagic.com/issues/show_bug.cgi?id=5519 Summary: Saner struct equality Product: D Version: D2 Platform: All OS/Version: All Status: NEW Severity: enhancement Priority: P2 Component: DMD AssignedTo: nobody puremagic.com ReportedBy: bearophile_hugs eml.cc Performing a comparison between two structs is a very common operation. Often structs contain strings and other things. Currently (DMD 2.051) the struct equality ignores the contents of strings contained inside structs, and this behaviour is unacceptably bug-prone in a language like D that's otherwise oriented toward code safety. So in the following four programs I'd like the assertions to pass. ---------------- struct Foo { string s; } void main() { string s1 = "he"; string s2 = "llo"; string s3 = "hel"; string s4 = "lo"; auto f1 = Foo(s1 ~ s2); auto f2 = Foo(s3 ~ s4); assert((s1 ~ s2) == (s3 ~ s4)); assert(f1 == f2); // this asserts } ---------------- struct Foo { int[] a; } void main() { auto a1 = [1, 2]; auto a2 = [3, 4, 5]; auto a3 = [1, 2, 3]; auto a4 = [4, 5]; auto f1 = Foo(a1 ~ a2); auto f2 = Foo(a3 ~ a4); assert((a1 ~ a2) == (a3 ~ a4)); assert(f1 == f2); // this asserts } ---------------- class Bar { int x; this(int x_) { x = x_; } bool opEquals(Object o) { return x == (cast(Bar)o).x; } } struct Foo { Bar a; } void main() { auto f1 = Foo(new Bar(1)); auto f2 = Foo(new Bar(1)); assert(f1 == f2); // this asserts } ---------------- struct Foo { int[int] aa; } void main() { auto f1 = Foo([1:0, 2:0]); auto f2 = Foo([1:0, 2:0]); assert(f1.aa == f2.aa); assert(f1 == f2); // this asserts } ---------------- The title of this enhancement request is "Saner struct equality" instead of "Sane struct equality" because (it seems) D struct equality isn't meant/expected to be fully correct. This example shows a struct comparison problem this enhancement request doesn't cover: import core.stdc.string: memset; struct Foo { long l; byte b; } void main() { Foo f1 = Foo(10, 20); Foo f2; memset(&f1, 'X', Foo.sizeof); f2.l = 10; f2.b = 20; assert(f1 == f2); // this asserts } ---------------- Surprisingly this works (DMD 2.051): struct Bar { int x; const bool opEquals(ref const(Bar) o) { return x == o.x || x == -o.x; } } struct Foo { Bar a; } void main() { auto f1 = Foo(Bar(1)); auto f2 = Foo(Bar(-1)); assert(f1 == f2); // this doesn't assert } ---------------- -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 02 2011
http://d.puremagic.com/issues/show_bug.cgi?id=5519 Denis Derman <denis.spir gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |denis.spir gmail.com ---Performing a comparison between two structs is a very common operation. Often structs contain strings and other things. Currently (DMD 2.051) the struct equality ignores the contents of strings contained inside structs, [...] ---------------- struct Foo { string s; } void main() { string s1 = "he"; string s2 = "llo"; string s3 = "hel"; string s4 = "lo"; auto f1 = Foo(s1 ~ s2); auto f2 = Foo(s3 ~ s4); assert((s1 ~ s2) == (s3 ~ s4)); assert(f1 == f2); // this asserts } ----------------This issue is partially masked by the fact sring /literals/ are interned in D (or is it an implementation optimisation of dmd?). Very annoying in interaction with the present issue: since string interning makes comparison of structs holding string sometimes behave as expected, other cases have an increased risk of being bug-prone. Below another example showing this (all asserts pass). Note that idup does not help & restore correct behaviour observed in case of literals: struct S {string s;} unittest { // literals string s01 = "hello"; string s02 = "hello"; assert ( S(s01) == S(s02) ); // concat string s1 = "he"; string s2 = "llo"; string s3 = "hel"; string s4 = "lo"; assert ( S(s1 ~ s2) != S(s3 ~ s4) ); // slice string s = "hello"; assert ( S(s[1..$-1]) != S("ell") ); // idup'ed assert ( S(s[1..$-1].idup) != S("ell") ); s5 = s[1..$-1].idup; assert ( S(s5) != S("ell") ); } Denis---------------- Surprisingly this works (DMD 2.051): struct Bar { int x; const bool opEquals(ref const(Bar) o) { return x == o.x || x == -o.x; } } struct Foo { Bar a; } void main() { auto f1 = Foo(Bar(1)); auto f2 = Foo(Bar(-1)); assert(f1 == f2); // this doesn't assert } ------------------ Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2011
http://d.puremagic.com/issues/show_bug.cgi?id=5519 ---Surprisingly this works (DMD 2.051): struct Bar { int x; const bool opEquals(ref const(Bar) o) { return x == o.x || x == -o.x; } } struct Foo { Bar a; } void main() { auto f1 = Foo(Bar(1)); auto f2 = Foo(Bar(-1)); assert(f1 == f2); // this doesn't assert }You probably meant in your comment: "this doesn't throw" didn't you? The assertion passes, meaning the code behaves with expected semantics. Denis -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2011
http://d.puremagic.com/issues/show_bug.cgi?id=5519 --- The core issue, I guess, is that '==' implicitely means comparison "by value equality". This sense is even more obvious in D which has a dedicated operator 'is' for comparison "by ref identity". Current behaviour comparing structs using by ref for their string & array members may not be wrong in itself, but it conflicts with expected semantics of '=='. And the case of (interned) string literals makes it inconsistent. Denis -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2011
http://d.puremagic.com/issues/show_bug.cgi?id=5519 --- One (hopefully last) more point: A situation where one may constantly need to compare structs for equality (by value!) is unittests: unittest { ... assert (result == S(a, b)); } This is actually how I stepped on the present issue. The general workaround is indeed to write custom opEquals methods which just compare member per member. Thus, I ended up adding such opEquals to all structs: struct Lexeme { string tag; string slice; Ordinal index; // for parser match error messages string toString () { return format(`Lexeme("%s","%s",%s)`, tag,slice,index); } // workaround for bug in default struct '==' const bool opEquals (ref const(Lexeme) l) { return ( this.tag == l.tag && this.slice == l.slice && this.index == l.index ); } } Denis -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 03 2011
http://d.puremagic.com/issues/show_bug.cgi?id=5519 This issue is essentially a dupe of bug 3789 -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 04 2011
http://d.puremagic.com/issues/show_bug.cgi?id=5519 Simen Kjaeraas <simen.kjaras gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |RESOLVED CC| |simen.kjaras gmail.com Resolution| |DUPLICATE PST --- *** This issue has been marked as a duplicate of issue 3789 *** -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Feb 04 2011