]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: given typedef struct S T, make C.T and C.struct_S interchangeable
authorRuss Cox <rsc@golang.org>
Wed, 28 May 2014 18:04:31 +0000 (14:04 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 28 May 2014 18:04:31 +0000 (14:04 -0400)
For incomplete struct S, C.T and C.struct_S were interchangeable in Go 1.2
and earlier, because all incomplete types were interchangeable
(even C.struct_S1 and C.struct_S2).

CL 76450043, which fixed issue 7409, made different incomplete types
different from Go's point of view, so that they were no longer completely
interchangeable.

However, imprecision about C.T and C.struct_S - really the same
underlying C type - is the one behavior enabled by the bug that
is most likely to be depended on by existing cgo code.
Explicitly allow it, to keep that code working.

Fixes #7786.

LGTM=iant, r
R=golang-codereviews, iant, r
CC=golang-codereviews
https://golang.org/cl/98580046

doc/go1.3.html
misc/cgo/test/issue7786.go [new file with mode: 0644]
src/cmd/cgo/gcc.go

index 9a9f9f8d4673228e30c3a7eaea9a3dca97bf4e6b..f4e055ae80a5e9c34fde4159262853b08ec96858 100644 (file)
@@ -253,7 +253,13 @@ with the effect that the Go compiler could not diagnose passing one kind of stru
 to a function expecting another.
 Go 1.3 corrects this mistake by translating each different
 incomplete struct to a different named type.
-However, some Go code took advantage of this bug to pass (for example) a <code>*C.FILE</code>
+</p>
+
+<p>
+Given the C declaration <code>typedef struct S T</code> for an incomplete <code>struct S</code>,
+some Go code used this bug to refer to the types <code>C.struct_S</code> and <code>C.T</code> interchangeably.
+Cgo now explicitly allows this use, even for completed struct types.
+However, some Go code also used this bug to pass (for example) a <code>*C.FILE</code>
 from one package to another.
 This is not legal and no longer works: in general Go packages
 should avoid exposing C types and names in their APIs.
diff --git a/misc/cgo/test/issue7786.go b/misc/cgo/test/issue7786.go
new file mode 100644 (file)
index 0000000..b927637
--- /dev/null
@@ -0,0 +1,51 @@
+// 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 7786. No runtime test, just make sure that typedef and struct/union/class are interchangeable at compile time.
+
+package cgotest
+
+// struct test7786;
+// typedef struct test7786 typedef_test7786;
+// void f7786(struct test7786 *ctx) {}
+// void g7786(typedef_test7786 *ctx) {}
+//
+// typedef struct body7786 typedef_body7786;
+// struct body7786 { int x; };
+// void b7786(struct body7786 *ctx) {}
+// void c7786(typedef_body7786 *ctx) {}
+//
+// typedef union union7786 typedef_union7786;
+// void u7786(union union7786 *ctx) {}
+// void v7786(typedef_union7786 *ctx) {}
+import "C"
+
+func f() {
+       var x1 *C.typedef_test7786
+       var x2 *C.struct_test7786
+       x1 = x2
+       x2 = x1
+       C.f7786(x1)
+       C.f7786(x2)
+       C.g7786(x1)
+       C.g7786(x2)
+
+       var b1 *C.typedef_body7786
+       var b2 *C.struct_body7786
+       b1 = b2
+       b2 = b1
+       C.b7786(b1)
+       C.b7786(b2)
+       C.c7786(b1)
+       C.c7786(b2)
+
+       var u1 *C.typedef_union7786
+       var u2 *C.union_union7786
+       u1 = u2
+       u2 = u1
+       C.u7786(u1)
+       C.u7786(u2)
+       C.v7786(u1)
+       C.v7786(u2)
+}
index c5fcdfc3df57e2217e7ca5535efa77a582c891fb..e403f6f5109f14f885ddaf784d242ae7c290d171 100644 (file)
@@ -1197,12 +1197,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
                return t
 
        case *dwarf.StructType:
-               if dt.ByteSize < 0 { // opaque struct
-                       break
-               }
                // Convert to Go struct, being careful about alignment.
                // Have to give it a name to simulate C "struct foo" references.
                tag := dt.StructName
+               if dt.ByteSize < 0 && tag == "" { // opaque unnamed struct - should not be possible
+                       break
+               }
                if tag == "" {
                        tag = "__" + strconv.Itoa(tagGen)
                        tagGen++
@@ -1212,6 +1212,16 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
                name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
                t.Go = name // publish before recursive calls
                goIdent[name.Name] = name
+               if dt.ByteSize < 0 {
+                       // Size calculation in c.Struct/c.Opaque will die with size=-1 (unknown),
+                       // so execute the basic things that the struct case would do
+                       // other than try to determine a Go representation.
+                       tt := *t
+                       tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}}
+                       tt.Go = c.Ident("struct{}")
+                       typedef[name.Name] = &tt
+                       break
+               }
                switch dt.Kind {
                case "class", "union":
                        t.Go = c.Opaque(t.Size)
@@ -1264,7 +1274,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
                        tt.Go = sub.Go
                        typedef[name.Name] = &tt
                }
-               if *godefs || *cdefs {
+
+               // If sub.Go.Name is "_Ctype_struct_foo" or "_Ctype_union_foo" or "_Ctype_class_foo",
+               // use that as the Go form for this typedef too, so that the typedef will be interchangeable
+               // with the base type.
+               // In -godefs and -cdefs mode, do this for all typedefs.
+               if isStructUnionClass(sub.Go) || *godefs || *cdefs {
                        t.Go = sub.Go
                }
 
@@ -1327,10 +1342,19 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
                // be correct, so calling dtype.Size again will produce the correct value.
                t.Size = dtype.Size()
                if t.Size < 0 {
-                       // Unsized types are [0]byte, unless they're typedefs of other types.
-                       // if so, use the name of the typedef for the go name.
+                       // Unsized types are [0]byte, unless they're typedefs of other types
+                       // or structs with tags.
+                       // if so, use the name we've already defined.
                        t.Size = 0
-                       if _, ok := dtype.(*dwarf.TypedefType); !ok {
+                       switch dt := dtype.(type) {
+                       case *dwarf.TypedefType:
+                               // ok
+                       case *dwarf.StructType:
+                               if dt.StructName != "" {
+                                       break
+                               }
+                               t.Go = c.Opaque(0)
+                       default:
                                t.Go = c.Opaque(0)
                        }
                        if t.C.Empty() {
@@ -1347,6 +1371,19 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
        return t
 }
 
+// isStructUnionClass reports whether the type described by the Go syntax x
+// is a struct, union, or class with a tag.
+func isStructUnionClass(x ast.Expr) bool {
+       id, ok := x.(*ast.Ident)
+       if !ok {
+               return false
+       }
+       name := id.Name
+       return strings.HasPrefix(name, "_Ctype_struct_") ||
+               strings.HasPrefix(name, "_Ctype_union_") ||
+               strings.HasPrefix(name, "_Ctype_class_")
+}
+
 // FuncArg returns a Go type with the same memory layout as
 // dtype when used as the type of a C function argument.
 func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {