]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/gc: fix Offsetof computation.
authorRémy Oudompheng <oudomphe@phare.normalesup.org>
Fri, 5 Apr 2013 19:24:07 +0000 (21:24 +0200)
committerRémy Oudompheng <oudomphe@phare.normalesup.org>
Fri, 5 Apr 2013 19:24:07 +0000 (21:24 +0200)
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

src/cmd/gc/unsafe.c
test/fixedbugs/issue4909a.go [new file with mode: 0644]
test/fixedbugs/issue4909b.go [new file with mode: 0644]
test/sizeof.go

index 95200ad415e41b475ee1d529c420c8020d0366e7..6b26cde2661d8ac499719e8ef3ec87fdc1da6776 100644 (file)
@@ -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) {
diff --git a/test/fixedbugs/issue4909a.go b/test/fixedbugs/issue4909a.go
new file mode 100644 (file)
index 0000000..aefe2d6
--- /dev/null
@@ -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"
diff --git a/test/fixedbugs/issue4909b.go b/test/fixedbugs/issue4909b.go
new file mode 100644 (file)
index 0000000..0f594e3
--- /dev/null
@@ -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")
+       }
+}
index a6abdd5c65727e5f5c03df72b5341df3aa099abe..9aa95677d474a6ff713d1da17b110dc63a44585a 100644 (file)
@@ -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")
+       }
 }