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.
This commit is contained in:
Ayke van Laethem
2019-04-27 23:06:45 +02:00
committed by Ron Evans
parent 4bd1b9e53d
commit 35af33ead7
5 changed files with 88 additions and 64 deletions
+6 -1
View File
@@ -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,
+64 -55
View File
@@ -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