mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/cgo: given typedef struct S T, make C.T and C.struct_S interchangeable
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
This commit is contained in:
parent
94849d5a78
commit
0782ee3ad5
3 changed files with 102 additions and 8 deletions
|
|
@ -253,7 +253,13 @@ with the effect that the Go compiler could not diagnose passing one kind of stru
|
||||||
to a function expecting another.
|
to a function expecting another.
|
||||||
Go 1.3 corrects this mistake by translating each different
|
Go 1.3 corrects this mistake by translating each different
|
||||||
incomplete struct to a different named type.
|
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.
|
from one package to another.
|
||||||
This is not legal and no longer works: in general Go packages
|
This is not legal and no longer works: in general Go packages
|
||||||
should avoid exposing C types and names in their APIs.
|
should avoid exposing C types and names in their APIs.
|
||||||
|
|
|
||||||
51
misc/cgo/test/issue7786.go
Normal file
51
misc/cgo/test/issue7786.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -1197,12 +1197,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||||
return t
|
return t
|
||||||
|
|
||||||
case *dwarf.StructType:
|
case *dwarf.StructType:
|
||||||
if dt.ByteSize < 0 { // opaque struct
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Convert to Go struct, being careful about alignment.
|
// Convert to Go struct, being careful about alignment.
|
||||||
// Have to give it a name to simulate C "struct foo" references.
|
// Have to give it a name to simulate C "struct foo" references.
|
||||||
tag := dt.StructName
|
tag := dt.StructName
|
||||||
|
if dt.ByteSize < 0 && tag == "" { // opaque unnamed struct - should not be possible
|
||||||
|
break
|
||||||
|
}
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag = "__" + strconv.Itoa(tagGen)
|
tag = "__" + strconv.Itoa(tagGen)
|
||||||
tagGen++
|
tagGen++
|
||||||
|
|
@ -1212,6 +1212,16 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||||
name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
|
name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
|
||||||
t.Go = name // publish before recursive calls
|
t.Go = name // publish before recursive calls
|
||||||
goIdent[name.Name] = name
|
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 {
|
switch dt.Kind {
|
||||||
case "class", "union":
|
case "class", "union":
|
||||||
t.Go = c.Opaque(t.Size)
|
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
|
tt.Go = sub.Go
|
||||||
typedef[name.Name] = &tt
|
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
|
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.
|
// be correct, so calling dtype.Size again will produce the correct value.
|
||||||
t.Size = dtype.Size()
|
t.Size = dtype.Size()
|
||||||
if t.Size < 0 {
|
if t.Size < 0 {
|
||||||
// Unsized types are [0]byte, unless they're typedefs of other types.
|
// Unsized types are [0]byte, unless they're typedefs of other types
|
||||||
// if so, use the name of the typedef for the go name.
|
// or structs with tags.
|
||||||
|
// if so, use the name we've already defined.
|
||||||
t.Size = 0
|
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)
|
t.Go = c.Opaque(0)
|
||||||
}
|
}
|
||||||
if t.C.Empty() {
|
if t.C.Empty() {
|
||||||
|
|
@ -1347,6 +1371,19 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||||
return t
|
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
|
// FuncArg returns a Go type with the same memory layout as
|
||||||
// dtype when used as the type of a C function argument.
|
// dtype when used as the type of a C function argument.
|
||||||
func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
|
func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue