mirror of
https://github.com/golang/go
synced 2024-10-14 11:53:56 +00:00
cmd/gc: fix handling of struct padding in hash/eq.
The test case of issue 4585 was not passing due to miscalculation of memequal args, and the previous fix does not handle padding at the end of a struct. Handling of padding at end of structs also fixes the case of [n]T where T is such a padded struct. Fixes #4585. (again) R=golang-dev, rsc CC=golang-dev https://golang.org/cl/7133059
This commit is contained in:
parent
47568c747d
commit
1d6eb2e9fa
|
@ -504,12 +504,17 @@ nod(int op, Node *nleft, Node *nright)
|
|||
return n;
|
||||
}
|
||||
|
||||
// ispaddedfield returns whether the given field
|
||||
// is followed by padding. For the case where t is
|
||||
// the last field, total gives the size of the enclosing struct.
|
||||
static int
|
||||
ispaddedfield(Type *t)
|
||||
ispaddedfield(Type *t, vlong total)
|
||||
{
|
||||
if(t->etype != TFIELD)
|
||||
fatal("ispaddedfield called non-field %T", t);
|
||||
return t->down != T && t->width + t->type->width != t->down->width;
|
||||
if(t->down == T)
|
||||
return t->width + t->type->width != total;
|
||||
return t->width + t->type->width != t->down->width;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -591,7 +596,7 @@ algtype1(Type *t, Type **bad)
|
|||
for(t1=t->type; t1!=T; t1=t1->down) {
|
||||
// Blank fields and padding must be ignored,
|
||||
// so need special compare.
|
||||
if(isblanksym(t1->sym) || ispaddedfield(t1)) {
|
||||
if(isblanksym(t1->sym) || ispaddedfield(t1, t->width)) {
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
|
@ -2619,7 +2624,7 @@ genhash(Sym *sym, Type *t)
|
|||
Node *hashel;
|
||||
Type *first, *t1;
|
||||
int old_safemode;
|
||||
int64 size, mul;
|
||||
int64 size, mul, offend;
|
||||
|
||||
if(debug['r'])
|
||||
print("genhash %S %T\n", sym, t);
|
||||
|
@ -2705,24 +2710,21 @@ genhash(Sym *sym, Type *t)
|
|||
// Walk the struct using memhash for runs of AMEM
|
||||
// and calling specific hash functions for the others.
|
||||
first = T;
|
||||
offend = 0;
|
||||
for(t1=t->type;; t1=t1->down) {
|
||||
if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
|
||||
offend = t1->width + t1->type->width;
|
||||
if(first == T)
|
||||
first = t1;
|
||||
// If it's a memory field but it's padded, stop here.
|
||||
if(ispaddedfield(t1))
|
||||
if(ispaddedfield(t1, t->width))
|
||||
t1 = t1->down;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
// Run memhash for fields up to this one.
|
||||
if(first != T) {
|
||||
if(first->down == t1)
|
||||
size = first->type->width;
|
||||
else if(t1 == T)
|
||||
size = t->width - first->width; // first->width is offset
|
||||
else
|
||||
size = t1->width - first->width; // both are offsets
|
||||
size = offend - first->width; // first->width is offset
|
||||
hashel = hashmem(first->type);
|
||||
// hashel(h, size, &p.first)
|
||||
call = nod(OCALL, hashel, N);
|
||||
|
@ -2856,6 +2858,7 @@ geneq(Sym *sym, Type *t)
|
|||
Type *t1, *first;
|
||||
int old_safemode;
|
||||
int64 size;
|
||||
int64 offend;
|
||||
|
||||
if(debug['r'])
|
||||
print("geneq %S %T\n", sym, t);
|
||||
|
@ -2929,12 +2932,14 @@ geneq(Sym *sym, Type *t)
|
|||
// and calling specific equality tests for the others.
|
||||
// Skip blank-named fields.
|
||||
first = T;
|
||||
offend = 0;
|
||||
for(t1=t->type;; t1=t1->down) {
|
||||
if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
|
||||
offend = t1->width + t1->type->width;
|
||||
if(first == T)
|
||||
first = t1;
|
||||
// If it's a memory field but it's padded, stop here.
|
||||
if(ispaddedfield(t1))
|
||||
if(ispaddedfield(t1, t->width))
|
||||
t1 = t1->down;
|
||||
else
|
||||
continue;
|
||||
|
@ -2952,10 +2957,7 @@ geneq(Sym *sym, Type *t)
|
|||
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
||||
} else {
|
||||
// More than two fields: use memequal.
|
||||
if(t1 == T)
|
||||
size = t->width - first->width; // first->width is offset
|
||||
else
|
||||
size = t1->width - first->width; // both are offsets
|
||||
size = offend - first->width; // first->width is offset
|
||||
fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq));
|
||||
}
|
||||
first = T;
|
||||
|
|
|
@ -32,6 +32,20 @@ type USmall struct {
|
|||
A, _, B int32
|
||||
}
|
||||
|
||||
// V has padding but not on the first field.
|
||||
type V struct {
|
||||
A1, A2, A3 int32
|
||||
B int16
|
||||
C int32
|
||||
}
|
||||
|
||||
// W has padding at the end.
|
||||
type W struct {
|
||||
A1, A2, A3 int32
|
||||
B int32
|
||||
C int8
|
||||
}
|
||||
|
||||
func test1() {
|
||||
var a, b U
|
||||
m := make(map[U]int)
|
||||
|
@ -84,8 +98,54 @@ func test3() {
|
|||
}
|
||||
}
|
||||
|
||||
func test4() {
|
||||
var a, b V
|
||||
m := make(map[V]int)
|
||||
|
||||
copy((*[20]byte)(unsafe.Pointer(&a))[:], "Hello World, Gopher!")
|
||||
a.A1, a.A2, a.A3, a.B, a.C = 1, 2, 3, 4, 5
|
||||
b.A1, b.A2, b.A3, b.B, b.C = 1, 2, 3, 4, 5
|
||||
|
||||
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 test5() {
|
||||
var a, b W
|
||||
m := make(map[W]int)
|
||||
|
||||
copy((*[20]byte)(unsafe.Pointer(&a))[:], "Hello World, Gopher!")
|
||||
a.A1, a.A2, a.A3, a.B, a.C = 1, 2, 3, 4, 5
|
||||
b.A1, b.A2, b.A3, b.B, b.C = 1, 2, 3, 4, 5
|
||||
|
||||
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 main() {
|
||||
test1()
|
||||
test2()
|
||||
test3()
|
||||
test4()
|
||||
test5()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue