mirror of
https://github.com/golang/go
synced 2024-11-25 11:27:56 -07:00
cmd/gc: fix Offsetof computation.
The offset of an embedded field s.X must be relative to s and not to the implicit s.Field of which X is a direct field. Moreover, no indirections may happen on the path. Fixes #4909. R=nigeltao, ality, daniel.morsing, iant, gri, r CC=golang-dev https://golang.org/cl/8287043
This commit is contained in:
parent
03048ae454
commit
2d3216f4a8
@ -16,7 +16,7 @@
|
||||
Node*
|
||||
unsafenmagic(Node *nn)
|
||||
{
|
||||
Node *r, *n;
|
||||
Node *r, *n, *base, *r1;
|
||||
Sym *s;
|
||||
Type *t, *tr;
|
||||
long v;
|
||||
@ -49,11 +49,43 @@ unsafenmagic(Node *nn)
|
||||
goto yes;
|
||||
}
|
||||
if(strcmp(s->name, "Offsetof") == 0) {
|
||||
typecheck(&r, Erv);
|
||||
if(r->op != ODOT && r->op != ODOTPTR)
|
||||
// must be a selector.
|
||||
if(r->op != OXDOT)
|
||||
goto bad;
|
||||
// Remember base of selector to find it back after dot insertion.
|
||||
// Since r->left may be mutated by typechecking, check it explicitly
|
||||
// first to track it correctly.
|
||||
typecheck(&r->left, Erv);
|
||||
base = r->left;
|
||||
typecheck(&r, Erv);
|
||||
v = r->xoffset;
|
||||
switch(r->op) {
|
||||
case ODOT:
|
||||
case ODOTPTR:
|
||||
break;
|
||||
case OCALLPART:
|
||||
yyerror("invalid expression %N: argument is a method value", nn);
|
||||
v = 0;
|
||||
goto ret;
|
||||
default:
|
||||
goto bad;
|
||||
}
|
||||
v = 0;
|
||||
// add offsets for inserted dots.
|
||||
for(r1=r; r1->left!=base; r1=r1->left) {
|
||||
switch(r1->op) {
|
||||
case ODOT:
|
||||
v += r1->xoffset;
|
||||
break;
|
||||
case ODOTPTR:
|
||||
yyerror("invalid expression %N: selector implies indirection of embedded %N", nn, r1->left);
|
||||
goto ret;
|
||||
default:
|
||||
dump("unsafenmagic", r);
|
||||
fatal("impossible %#O node after dot insertion", r1->op);
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
v += r1->xoffset;
|
||||
goto yes;
|
||||
}
|
||||
if(strcmp(s->name, "Alignof") == 0) {
|
||||
|
35
test/fixedbugs/issue4909a.go
Normal file
35
test/fixedbugs/issue4909a.go
Normal file
@ -0,0 +1,35 @@
|
||||
// errorcheck
|
||||
|
||||
// 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 4909: compiler incorrectly accepts unsafe.Offsetof(t.x)
|
||||
// where x is a field of an embedded pointer field.
|
||||
|
||||
package p
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
A int
|
||||
*B
|
||||
}
|
||||
|
||||
func (t T) Method() {}
|
||||
|
||||
type B struct {
|
||||
X, Y int
|
||||
}
|
||||
|
||||
var t T
|
||||
var p *T
|
||||
|
||||
const N1 = unsafe.Offsetof(t.X) // ERROR "indirection"
|
||||
const N2 = unsafe.Offsetof(p.X) // ERROR "indirection"
|
||||
const N3 = unsafe.Offsetof(t.B.X) // valid
|
||||
const N4 = unsafe.Offsetof(p.B.X) // valid
|
||||
const N5 = unsafe.Offsetof(t.Method) // ERROR "method value"
|
||||
const N6 = unsafe.Offsetof(p.Method) // ERROR "method value"
|
80
test/fixedbugs/issue4909b.go
Normal file
80
test/fixedbugs/issue4909b.go
Normal file
@ -0,0 +1,80 @@
|
||||
// errorcheckoutput
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// We are going to define 256 types T(n),
|
||||
// such that T(n) embeds T(2n) and *T(2n+1).
|
||||
|
||||
func main() {
|
||||
fmt.Printf("// errorcheck\n\n")
|
||||
fmt.Printf("package p\n\n")
|
||||
fmt.Println(`import "unsafe"`)
|
||||
|
||||
// Dump types.
|
||||
for n := 1; n < 256; n++ {
|
||||
writeStruct(n)
|
||||
}
|
||||
// Dump leaves
|
||||
for n := 256; n < 512; n++ {
|
||||
fmt.Printf("type T%d int\n", n)
|
||||
}
|
||||
|
||||
fmt.Printf("var t T1\n")
|
||||
fmt.Printf("var p *T1\n")
|
||||
|
||||
// Simple selectors
|
||||
for n := 2; n < 256; n++ {
|
||||
writeDot(n)
|
||||
}
|
||||
|
||||
// Double selectors
|
||||
for n := 128; n < 256; n++ {
|
||||
writeDot(n/16, n)
|
||||
}
|
||||
|
||||
// Triple selectors
|
||||
for n := 128; n < 256; n++ {
|
||||
writeDot(n/64, n/8, n)
|
||||
}
|
||||
}
|
||||
|
||||
const structTpl = `
|
||||
type T%d struct {
|
||||
A%d int
|
||||
T%d
|
||||
*T%d
|
||||
}
|
||||
`
|
||||
|
||||
func writeStruct(n int) {
|
||||
fmt.Printf(structTpl, n, n, 2*n, 2*n+1)
|
||||
}
|
||||
|
||||
func writeDot(ns ...int) {
|
||||
for _, root := range []string{"t", "p"} {
|
||||
fmt.Printf("const _ = unsafe.Offsetof(%s", root)
|
||||
for _, n := range ns {
|
||||
fmt.Printf(".T%d", n)
|
||||
}
|
||||
// Does it involve an indirection?
|
||||
nlast := ns[len(ns)-1]
|
||||
nprev := 1
|
||||
if len(ns) > 1 {
|
||||
nprev = ns[len(ns)-2]
|
||||
}
|
||||
isIndirect := false
|
||||
for n := nlast / 2; n > nprev; n /= 2 {
|
||||
if n%2 == 1 {
|
||||
isIndirect = true
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Print(")")
|
||||
if isIndirect {
|
||||
fmt.Print(` // ERROR "indirection"`)
|
||||
}
|
||||
fmt.Print("\n")
|
||||
}
|
||||
}
|
137
test/sizeof.go
137
test/sizeof.go
@ -4,8 +4,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test unsafe.Sizeof, unsafe.Alignof, and unsafe.Offsetof all return uintptr.
|
||||
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
@ -18,8 +16,143 @@ var t T
|
||||
|
||||
func isUintptr(uintptr) {}
|
||||
|
||||
type T2 struct {
|
||||
A int32
|
||||
U2
|
||||
}
|
||||
|
||||
type U2 struct {
|
||||
B int32
|
||||
C int32
|
||||
}
|
||||
|
||||
var t2 T2
|
||||
var p2 *T2
|
||||
|
||||
func main() {
|
||||
// Test unsafe.Sizeof, unsafe.Alignof, and unsafe.Offsetof all return uintptr.
|
||||
isUintptr(unsafe.Sizeof(t))
|
||||
isUintptr(unsafe.Alignof(t))
|
||||
isUintptr(unsafe.Offsetof(t.X))
|
||||
|
||||
// Test correctness of Offsetof with respect to embedded fields (issue 4909).
|
||||
if unsafe.Offsetof(t2.C) != 8 {
|
||||
println(unsafe.Offsetof(t2.C), "!= 8")
|
||||
panic("unsafe.Offsetof(t2.C) != 8")
|
||||
}
|
||||
if unsafe.Offsetof(p2.C) != 8 {
|
||||
println(unsafe.Offsetof(p2.C), "!= 8")
|
||||
panic("unsafe.Offsetof(p2.C) != 8")
|
||||
}
|
||||
if unsafe.Offsetof(t2.U2.C) != 4 {
|
||||
println(unsafe.Offsetof(t2.U2.C), "!= 4")
|
||||
panic("unsafe.Offsetof(t2.U2.C) != 4")
|
||||
}
|
||||
if unsafe.Offsetof(p2.U2.C) != 4 {
|
||||
println(unsafe.Offsetof(p2.U2.C), "!= 4")
|
||||
panic("unsafe.Offsetof(p2.U2.C) != 4")
|
||||
}
|
||||
testDeep()
|
||||
testNotEmbedded()
|
||||
}
|
||||
|
||||
type (
|
||||
S1 struct {
|
||||
A int32
|
||||
S2
|
||||
}
|
||||
S2 struct {
|
||||
B int32
|
||||
S3
|
||||
}
|
||||
S3 struct {
|
||||
C int32
|
||||
S4
|
||||
}
|
||||
S4 struct {
|
||||
D int32
|
||||
S5
|
||||
}
|
||||
S5 struct {
|
||||
E int32
|
||||
S6
|
||||
}
|
||||
S6 struct {
|
||||
F int32
|
||||
S7
|
||||
}
|
||||
S7 struct {
|
||||
G int32
|
||||
S8
|
||||
}
|
||||
S8 struct {
|
||||
H int32
|
||||
*S1
|
||||
}
|
||||
)
|
||||
|
||||
func testDeep() {
|
||||
var s1 S1
|
||||
switch {
|
||||
case unsafe.Offsetof(s1.A) != 0:
|
||||
panic("unsafe.Offsetof(s1.A) != 0")
|
||||
case unsafe.Offsetof(s1.B) != 4:
|
||||
panic("unsafe.Offsetof(s1.B) != 4")
|
||||
case unsafe.Offsetof(s1.C) != 8:
|
||||
panic("unsafe.Offsetof(s1.C) != 8")
|
||||
case unsafe.Offsetof(s1.D) != 12:
|
||||
panic("unsafe.Offsetof(s1.D) != 12")
|
||||
case unsafe.Offsetof(s1.E) != 16:
|
||||
panic("unsafe.Offsetof(s1.E) != 16")
|
||||
case unsafe.Offsetof(s1.F) != 20:
|
||||
panic("unsafe.Offsetof(s1.F) != 20")
|
||||
case unsafe.Offsetof(s1.G) != 24:
|
||||
panic("unsafe.Offsetof(s1.G) != 24")
|
||||
case unsafe.Offsetof(s1.H) != 28:
|
||||
panic("unsafe.Offsetof(s1.H) != 28")
|
||||
case unsafe.Offsetof(s1.S1) != 32:
|
||||
panic("unsafe.Offsetof(s1.S1) != 32")
|
||||
case unsafe.Offsetof(s1.S1.S2.S3.S4.S5.S6.S7.S8.S1.S2) != 4:
|
||||
panic("unsafe.Offsetof(s1.S1.S2.S3.S4.S5.S6.S7.S8.S1.S2) != 4")
|
||||
}
|
||||
}
|
||||
|
||||
func testNotEmbedded() {
|
||||
type T2 struct {
|
||||
B int32
|
||||
C int32
|
||||
}
|
||||
type T1 struct {
|
||||
A int32
|
||||
T2
|
||||
}
|
||||
type T struct {
|
||||
Dummy int32
|
||||
F T1
|
||||
P *T1
|
||||
}
|
||||
|
||||
var t T
|
||||
var p *T
|
||||
switch {
|
||||
case unsafe.Offsetof(t.F.B) != 4:
|
||||
panic("unsafe.Offsetof(t.F.B) != 4")
|
||||
case unsafe.Offsetof(t.F.C) != 8:
|
||||
panic("unsafe.Offsetof(t.F.C) != 8")
|
||||
|
||||
case unsafe.Offsetof(t.P.B) != 4:
|
||||
panic("unsafe.Offsetof(t.P.B) != 4")
|
||||
case unsafe.Offsetof(t.P.C) != 8:
|
||||
panic("unsafe.Offsetof(t.P.C) != 8")
|
||||
|
||||
case unsafe.Offsetof(p.F.B) != 4:
|
||||
panic("unsafe.Offsetof(p.F.B) != 4")
|
||||
case unsafe.Offsetof(p.F.C) != 8:
|
||||
panic("unsafe.Offsetof(p.F.C) != 8")
|
||||
|
||||
case unsafe.Offsetof(p.P.B) != 4:
|
||||
panic("unsafe.Offsetof(p.P.B) != 4")
|
||||
case unsafe.Offsetof(p.P.C) != 8:
|
||||
panic("unsafe.Offsetof(p.P.C) != 8")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user