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 {
|
for _, name := range names {
|
||||||
typedef := info.typedefs[name]
|
typedef := info.typedefs[name]
|
||||||
typeName := "C." + name
|
typeName := "C." + name
|
||||||
|
isAlias := true
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
typeName = "C." + name[len("_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 {
|
if _, ok := cgoAliases[typeName]; ok {
|
||||||
// This is a type that also exists in Go (defined in stdint.h).
|
// This is a type that also exists in Go (defined in stdint.h).
|
||||||
|
@ -377,6 +379,9 @@ func (info *fileInfo) addTypedefs() {
|
||||||
},
|
},
|
||||||
Type: typedef.typeExpr,
|
Type: typedef.typeExpr,
|
||||||
}
|
}
|
||||||
|
if isAlias {
|
||||||
|
typeSpec.Assign = info.importCPos
|
||||||
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
}
|
}
|
||||||
|
@ -400,7 +405,7 @@ func (info *fileInfo) addElaboratedTypes() {
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
typ := info.elaboratedTypes[name]
|
typ := info.elaboratedTypes[name]
|
||||||
typeName := "C.struct_" + name
|
typeName := "C." + name
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
Name: typeName,
|
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:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
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)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
return info.makeASTType(underlying, pos)
|
||||||
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,
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic("unknown elaborated type")
|
panic("unknown elaborated type")
|
||||||
}
|
}
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
fieldList := &ast.FieldList{
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
Opening: pos,
|
var cgoName string
|
||||||
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) {
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
return &ast.StructType{
|
cgoName = "struct_" + name
|
||||||
Struct: pos,
|
|
||||||
Fields: fieldList,
|
|
||||||
}
|
|
||||||
case C.CXCursor_UnionDecl:
|
case C.CXCursor_UnionDecl:
|
||||||
if len(fieldList.List) > 1 {
|
cgoName = "union_" + name
|
||||||
// Insert a special field at the front (of zero width) as a
|
default:
|
||||||
// marker that this is struct is actually a union. This is done
|
panic("unknown record declaration")
|
||||||
// by giving the field a name that cannot be expressed directly
|
}
|
||||||
// in Go.
|
if _, ok := info.elaboratedTypes[cgoName]; !ok {
|
||||||
// Other parts of the compiler look at the first element in a
|
info.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||||
// struct (of size > 2) to know whether this is a union.
|
fieldList := &ast.FieldList{
|
||||||
// Note that we don't have to insert it for single-element
|
Opening: pos,
|
||||||
// unions as they're basically equivalent to a struct.
|
Closing: pos,
|
||||||
unionMarker := &ast.Field{
|
}
|
||||||
Type: &ast.StructType{
|
ref := refMap.Put(struct {
|
||||||
Struct: pos,
|
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{
|
case C.CXCursor_UnionDecl:
|
||||||
&ast.Ident{
|
if len(fieldList.List) > 1 {
|
||||||
NamePos: pos,
|
// Insert a special field at the front (of zero width) as a
|
||||||
Name: "C union",
|
// marker that this is struct is actually a union. This is done
|
||||||
Obj: &ast.Object{
|
// by giving the field a name that cannot be expressed directly
|
||||||
Kind: ast.Var,
|
// in Go.
|
||||||
Name: "C union",
|
// Other parts of the compiler look at the first element in a
|
||||||
Decl: unionMarker,
|
// 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...)
|
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||||
}
|
Struct: pos,
|
||||||
return &ast.StructType{
|
Fields: fieldList,
|
||||||
Struct: pos,
|
}
|
||||||
Fields: fieldList,
|
default:
|
||||||
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + cgoName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if typeName == "" {
|
if typeName == "" {
|
||||||
// Fallback, probably incorrect but at least the error points to an odd
|
// 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"
|
import "unsafe"
|
||||||
|
|
||||||
func (s C.myint) Int() int {
|
|
||||||
return int(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("fortytwo:", C.fortytwo())
|
println("fortytwo:", C.fortytwo())
|
||||||
println("add:", C.add(C.int(3), 5))
|
println("add:", C.add(C.int(3), 5))
|
||||||
|
@ -33,6 +29,10 @@ func main() {
|
||||||
cb = C.binop_t(C.mul)
|
cb = C.binop_t(C.mul)
|
||||||
println("callback 2:", C.doCallback(20, 30, cb))
|
println("callback 2:", C.doCallback(20, 30, cb))
|
||||||
|
|
||||||
|
// equivalent types
|
||||||
|
var goInt8 int8 = 5
|
||||||
|
var _ C.int8_t = goInt8
|
||||||
|
|
||||||
// more globals
|
// more globals
|
||||||
println("bool:", C.globalBool, C.globalBool2 == true)
|
println("bool:", C.globalBool, C.globalBool2 == true)
|
||||||
println("float:", C.globalFloat)
|
println("float:", C.globalFloat)
|
||||||
|
@ -57,10 +57,14 @@ func main() {
|
||||||
C.unionSetData(5, 8, 1)
|
C.unionSetData(5, 8, 1)
|
||||||
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
||||||
println("union field:", printUnion(C.globalUnion).f)
|
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
|
// 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.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)}}
|
|
||||||
for list != nil {
|
for list != nil {
|
||||||
println("n in chain:", list.n)
|
println("n in chain:", list.n)
|
||||||
list = (*C.list_t)(list.next)
|
list = (*C.list_t)(list.next)
|
||||||
|
@ -70,7 +74,7 @@ func main() {
|
||||||
func printUnion(union C.joined_t) C.joined_t {
|
func printUnion(union C.joined_t) C.joined_t {
|
||||||
println("union local data: ", union.data[0], union.data[1], union.data[2])
|
println("union local data: ", union.data[0], union.data[1], union.data[2])
|
||||||
union.s = -33
|
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
|
union.f = 6.28
|
||||||
println("union f:", union.f)
|
println("union f:", union.f)
|
||||||
return union
|
return union
|
||||||
|
|
5
testdata/cgo/main.h
предоставленный
5
testdata/cgo/main.h
предоставленный
|
@ -15,6 +15,11 @@ typedef struct collection {
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
} collection_t;
|
} collection_t;
|
||||||
|
|
||||||
|
struct point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
// linked list
|
// linked list
|
||||||
typedef struct list_t {
|
typedef struct list_t {
|
||||||
int n;
|
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 f: +3.140000e+000
|
||||||
union global data: 5 8 1
|
union global data: 5 8 1
|
||||||
union local data: 5 8 1
|
union local data: 5 8 1
|
||||||
union s method: -33 false
|
union s: true
|
||||||
union f: +6.280000e+000
|
union f: +6.280000e+000
|
||||||
union field: +6.280000e+000
|
union field: +6.280000e+000
|
||||||
|
struct: 3 5
|
||||||
n in chain: 3
|
n in chain: 3
|
||||||
n in chain: 6
|
n in chain: 6
|
||||||
n in chain: 7
|
n in chain: 7
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче