diff --git a/doc/go1.3.html b/doc/go1.3.html
index 9a9f9f8d467..f4e055ae80a 100644
--- a/doc/go1.3.html
+++ b/doc/go1.3.html
@@ -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 *C.FILE
+
+Given the C declaration typedef struct S T
for an incomplete struct S
,
+some Go code used this bug to refer to the types C.struct_S
and C.T
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 *C.FILE
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
index 00000000000..b92763789b2
--- /dev/null
+++ b/misc/cgo/test/issue7786.go
@@ -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)
+}
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index c5fcdfc3df5..e403f6f5109 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -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 {