return n;
}
+static int
+ispaddedfield(Type *t)
+{
+ if(t->etype != TFIELD)
+ fatal("ispaddedfield called non-field %T", t);
+ return t->down != T && t->width + t->type->width != t->down->width;
+}
+
int
algtype1(Type *t, Type **bad)
{
}
ret = AMEM;
for(t1=t->type; t1!=T; t1=t1->down) {
- if(isblanksym(t1->sym))
+ // Blank fields and padding must be ignored,
+ // so need special compare.
+ if(isblanksym(t1->sym) || ispaddedfield(t1)) {
+ ret = -1;
continue;
+ }
a = algtype1(t1->type, bad);
if(a == ANOEQ)
return ANOEQ; // not comparable
// and calling specific hash functions for the others.
first = T;
for(t1=t->type;; t1=t1->down) {
- if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
- if(first == T && !isblanksym(t1->sym))
+ if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
+ if(first == T)
first = t1;
- continue;
+ // If it's a memory field but it's padded, stop here.
+ if(ispaddedfield(t1))
+ t1 = t1->down;
+ else
+ continue;
}
// Run memhash for fields up to this one.
- while(first != T && isblanksym(first->sym))
- first = first->down;
if(first != T) {
if(first->down == t1)
size = first->type->width;
}
if(t1 == T)
break;
+ if(isblanksym(t1->sym))
+ continue;
// Run hash for this field.
hashel = hashfor(t1->type);
call->list = list(call->list, na);
fn->nbody = list(fn->nbody, call);
}
+ // make sure body is not empty.
+ fn->nbody = list(fn->nbody, nod(ORETURN, N, N));
break;
}
case TSTRUCT:
// Walk the struct using memequal for runs of AMEM
// and calling specific equality tests for the others.
+ // Skip blank-named fields.
first = T;
for(t1=t->type;; t1=t1->down) {
- if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
- if(first == T && !isblanksym(t1->sym))
+ if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
+ if(first == T)
first = t1;
- continue;
+ // If it's a memory field but it's padded, stop here.
+ if(ispaddedfield(t1))
+ t1 = t1->down;
+ else
+ continue;
}
// Run memequal for fields up to this one.
// TODO(rsc): All the calls to newname are wrong for
// cross-package unexported fields.
- while(first != T && isblanksym(first->sym))
- first = first->down;
if(first != T) {
if(first->down == t1) {
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
}
if(t1 == T)
break;
+ if(isblanksym(t1->sym))
+ continue;
// Check this field, which is not just memory.
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq));
--- /dev/null
+// run
+
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 4585: comparisons and hashes process blank
+// fields and padding in structs.
+
+package main
+
+import "unsafe"
+
+// T is a structure with padding.
+type T struct {
+ A int16
+ B int64
+ C int16
+ D int64
+ Dummy [64]byte
+}
+
+// U is a structure with a blank field
+type U struct {
+ A, _, B int
+ Dummy [64]byte
+}
+
+// USmall is like U but the frontend will inline comparison
+// instead of calling the generated eq function.
+type USmall struct {
+ A, _, B int32
+}
+
+func test1() {
+ var a, b U
+ m := make(map[U]int)
+ copy((*[16]byte)(unsafe.Pointer(&a))[:], "hello world!")
+ a.A, a.B = 1, 2
+ b.A, b.B = 1, 2
+ if a != b {
+ panic("broken equality: a != b")
+ }
+
+ m[a] = 1
+ m[b] = 2
+ if len(m) == 2 {
+ panic("broken hash: len(m) == 2")
+ }
+ if m[a] != 2 {
+ panic("m[a] != 2")
+ }
+}
+
+func test2() {
+ var a, b T
+ m := make(map[T]int)
+
+ copy((*[16]byte)(unsafe.Pointer(&a))[:], "hello world!")
+ a.A, a.B, a.C, a.D = 1, 2, 3, 4
+ b.A, b.B, b.C, b.D = 1, 2, 3, 4
+
+ if a != b {
+ panic("broken equality: a != b")
+ }
+
+ m[a] = 1
+ m[b] = 2
+ if len(m) == 2 {
+ panic("broken hash: len(m) == 2")
+ }
+ if m[a] != 2 {
+ panic("m[a] != 2")
+ }
+}
+
+func test3() {
+ var a, b USmall
+ copy((*[12]byte)(unsafe.Pointer(&a))[:], "hello world!")
+ a.A, a.B = 1, 2
+ b.A, b.B = 1, 2
+ if a != b {
+ panic("broken equality: a != b")
+ }
+}
+
+func main() {
+ test1()
+ test2()
+ test3()
+}