cgo: improve typedef/struct/enum support
Typedefs are now Go type aliases. And C.struct_ and C.union_ prefixed records work correctly now, even when they're not in a typedef.
Этот коммит содержится в:
родитель
4bd1b9e53d
коммит
35af33ead7
5 изменённых файлов: 88 добавлений и 64 удалений
|
@ -358,8 +358,10 @@ func (info *fileInfo) addTypedefs() {
|
|||
for _, name := range names {
|
||||
typedef := info.typedefs[name]
|
||||
typeName := "C." + name
|
||||
isAlias := true
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
typeName = "C." + name[len("_Cgo_"):]
|
||||
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
||||
}
|
||||
if _, ok := cgoAliases[typeName]; ok {
|
||||
// This is a type that also exists in Go (defined in stdint.h).
|
||||
|
@ -377,6 +379,9 @@ func (info *fileInfo) addTypedefs() {
|
|||
},
|
||||
Type: typedef.typeExpr,
|
||||
}
|
||||
if isAlias {
|
||||
typeSpec.Assign = info.importCPos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
|
@ -400,7 +405,7 @@ func (info *fileInfo) addElaboratedTypes() {
|
|||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typ := info.elaboratedTypes[name]
|
||||
typeName := "C.struct_" + name
|
||||
typeName := "C." + name
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
|
|
|
@ -197,6 +197,9 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||
},
|
||||
}
|
||||
}
|
||||
case C.CXCursor_StructDecl:
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
info.makeASTType(typ, pos)
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
|
@ -400,74 +403,80 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||
underlying := C.clang_Type_getNamedType(typ)
|
||||
switch underlying.kind {
|
||||
case C.CXType_Record:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
// It is possible that this is a recursive definition, for example
|
||||
// in linked lists (structs contain a pointer to the next element
|
||||
// of the same type). If the name exists in info.elaboratedTypes,
|
||||
// it is being processed, although it may not be fully defined yet.
|
||||
if _, ok := info.elaboratedTypes[name]; !ok {
|
||||
info.elaboratedTypes[name] = nil // predeclare (to avoid endless recursion)
|
||||
info.elaboratedTypes[name] = info.makeASTType(underlying, info.getCursorPosition(cursor))
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C.struct_" + name,
|
||||
}
|
||||
return info.makeASTType(underlying, pos)
|
||||
default:
|
||||
panic("unknown elaborated type")
|
||||
}
|
||||
case C.CXType_Record:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
}
|
||||
ref := refMap.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
info *fileInfo
|
||||
}{fieldList, info})
|
||||
defer refMap.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
var cgoName string
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}
|
||||
cgoName = "struct_" + name
|
||||
case C.CXCursor_UnionDecl:
|
||||
if len(fieldList.List) > 1 {
|
||||
// Insert a special field at the front (of zero width) as a
|
||||
// marker that this is struct is actually a union. This is done
|
||||
// by giving the field a name that cannot be expressed directly
|
||||
// in Go.
|
||||
// Other parts of the compiler look at the first element in a
|
||||
// struct (of size > 2) to know whether this is a union.
|
||||
// Note that we don't have to insert it for single-element
|
||||
// unions as they're basically equivalent to a struct.
|
||||
unionMarker := &ast.Field{
|
||||
Type: &ast.StructType{
|
||||
Struct: pos,
|
||||
},
|
||||
cgoName = "union_" + name
|
||||
default:
|
||||
panic("unknown record declaration")
|
||||
}
|
||||
if _, ok := info.elaboratedTypes[cgoName]; !ok {
|
||||
info.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
}
|
||||
ref := refMap.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
info *fileInfo
|
||||
}{fieldList, info})
|
||||
defer refMap.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}
|
||||
unionMarker.Names = []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C union",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C union",
|
||||
Decl: unionMarker,
|
||||
case C.CXCursor_UnionDecl:
|
||||
if len(fieldList.List) > 1 {
|
||||
// Insert a special field at the front (of zero width) as a
|
||||
// marker that this is struct is actually a union. This is done
|
||||
// by giving the field a name that cannot be expressed directly
|
||||
// in Go.
|
||||
// Other parts of the compiler look at the first element in a
|
||||
// struct (of size > 2) to know whether this is a union.
|
||||
// Note that we don't have to insert it for single-element
|
||||
// unions as they're basically equivalent to a struct.
|
||||
unionMarker := &ast.Field{
|
||||
Type: &ast.StructType{
|
||||
Struct: pos,
|
||||
},
|
||||
},
|
||||
}
|
||||
unionMarker.Names = []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C union",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C union",
|
||||
Decl: unionMarker,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||
}
|
||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||
}
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
}
|
||||
if typeName == "" {
|
||||
// Fallback, probably incorrect but at least the error points to an odd
|
||||
|
|
18
testdata/cgo/main.go
предоставленный
18
testdata/cgo/main.go
предоставленный
|
@ -9,10 +9,6 @@ import "C"
|
|||
|
||||
import "unsafe"
|
||||
|
||||
func (s C.myint) Int() int {
|
||||
return int(s)
|
||||
}
|
||||
|
||||
func main() {
|
||||
println("fortytwo:", C.fortytwo())
|
||||
println("add:", C.add(C.int(3), 5))
|
||||
|
@ -33,6 +29,10 @@ func main() {
|
|||
cb = C.binop_t(C.mul)
|
||||
println("callback 2:", C.doCallback(20, 30, cb))
|
||||
|
||||
// equivalent types
|
||||
var goInt8 int8 = 5
|
||||
var _ C.int8_t = goInt8
|
||||
|
||||
// more globals
|
||||
println("bool:", C.globalBool, C.globalBool2 == true)
|
||||
println("float:", C.globalFloat)
|
||||
|
@ -57,10 +57,14 @@ func main() {
|
|||
C.unionSetData(5, 8, 1)
|
||||
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
||||
println("union field:", printUnion(C.globalUnion).f)
|
||||
var _ C.union_joined = C.globalUnion
|
||||
|
||||
// elaborated type
|
||||
p := C.struct_point{x: 3, y: 5}
|
||||
println("struct:", p.x, p.y)
|
||||
|
||||
// recursive types, test using a linked list
|
||||
lastElement := &C.list_t{n: 7, next: nil}
|
||||
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: (*C.struct_list_t)(lastElement)}}
|
||||
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: &C.list_t{n: 7, next: nil}}}
|
||||
for list != nil {
|
||||
println("n in chain:", list.n)
|
||||
list = (*C.list_t)(list.next)
|
||||
|
@ -70,7 +74,7 @@ func main() {
|
|||
func printUnion(union C.joined_t) C.joined_t {
|
||||
println("union local data: ", union.data[0], union.data[1], union.data[2])
|
||||
union.s = -33
|
||||
println("union s method:", union.s.Int(), union.data[0] == 5)
|
||||
println("union s:", union.data[0] == -33)
|
||||
union.f = 6.28
|
||||
println("union f:", union.f)
|
||||
return union
|
||||
|
|
5
testdata/cgo/main.h
предоставленный
5
testdata/cgo/main.h
предоставленный
|
@ -15,6 +15,11 @@ typedef struct collection {
|
|||
unsigned char c;
|
||||
} collection_t;
|
||||
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// linked list
|
||||
typedef struct list_t {
|
||||
int n;
|
||||
|
|
3
testdata/cgo/out.txt
предоставленный
3
testdata/cgo/out.txt
предоставленный
|
@ -24,9 +24,10 @@ union s: 22
|
|||
union f: +3.140000e+000
|
||||
union global data: 5 8 1
|
||||
union local data: 5 8 1
|
||||
union s method: -33 false
|
||||
union s: true
|
||||
union f: +6.280000e+000
|
||||
union field: +6.280000e+000
|
||||
struct: 3 5
|
||||
n in chain: 3
|
||||
n in chain: 6
|
||||
n in chain: 7
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче