aboutsummaryrefslogtreecommitdiffhomepage
path: root/cgo
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-06-02 16:22:27 +0200
committerRon Evans <[email protected]>2019-06-03 20:01:47 +0200
commit0ce4d907797d1bdcd8b99606d2e0139e06eaf31a (patch)
tree4d20baf2389451212ff022c241065575dddb20a3 /cgo
parent1047c9bd05b4cbd5491f5dc25904065aa926a65c (diff)
downloadtinygo-0ce4d907797d1bdcd8b99606d2e0139e06eaf31a.tar.gz
tinygo-0ce4d907797d1bdcd8b99606d2e0139e06eaf31a.zip
cgo: add support for anonymous structs
Diffstat (limited to 'cgo')
-rw-r--r--cgo/cgo.go3
-rw-r--r--cgo/libclang.go182
2 files changed, 106 insertions, 79 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go
index 3d02defc7..856149c20 100644
--- a/cgo/cgo.go
+++ b/cgo/cgo.go
@@ -36,6 +36,7 @@ type cgoPackage struct {
typedefs map[string]*typedefInfo
elaboratedTypes map[string]*elaboratedTypeInfo
enums map[string]enumInfo
+ anonStructNum int
}
// constantInfo stores some information about a CGo constant found by libclang
@@ -68,7 +69,7 @@ type typedefInfo struct {
// elaboratedTypeInfo contains some information about an elaborated type
// (struct, union) found in the C AST.
type elaboratedTypeInfo struct {
- typeExpr ast.Expr
+ typeExpr *ast.StructType
pos token.Pos
bitfields []bitfieldInfo
}
diff --git a/cgo/libclang.go b/cgo/libclang.go
index b2104296b..5bc3a1b1b 100644
--- a/cgo/libclang.go
+++ b/cgo/libclang.go
@@ -513,92 +513,48 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
case C.CXType_Record:
cursor := C.tinygo_clang_getTypeDeclaration(typ)
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
- var cgoName string
- switch C.tinygo_clang_getCursorKind(cursor) {
- case C.CXCursor_StructDecl:
- cgoName = "struct_" + name
- case C.CXCursor_UnionDecl:
- cgoName = "union_" + name
- default:
- panic("unknown record declaration")
- }
- if _, ok := p.elaboratedTypes[cgoName]; !ok {
- p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
- fieldList := &ast.FieldList{
- Opening: pos,
- Closing: pos,
- }
- var bitfieldList []bitfieldInfo
- inBitfield := false
- bitfieldNum := 0
- ref := storedRefs.Put(struct {
- fieldList *ast.FieldList
- pkg *cgoPackage
- inBitfield *bool
- bitfieldNum *int
- bitfieldList *[]bitfieldInfo
- }{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
- defer storedRefs.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:
+ if name == "" {
+ // Anonymous record, probably inside a typedef.
+ typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
+ if bitfieldList != nil {
+ // This struct has bitfields, so we have to declare it as a
+ // named type (for bitfield getters/setters to work).
+ p.anonStructNum++
+ cgoName := "struct_" + strconv.Itoa(p.anonStructNum)
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
- typeExpr: &ast.StructType{
- Struct: pos,
- Fields: fieldList,
- },
+ typeExpr: typeExpr,
pos: pos,
bitfields: bitfieldList,
}
- case C.CXCursor_UnionDecl:
- if bitfieldList != nil {
- // This is valid C... but please don't do this.
- p.errors = append(p.errors, scanner.Error{
- Pos: p.fset.PositionFor(pos, true),
- Msg: fmt.Sprintf("bitfield in a union is not supported"),
- })
- }
- 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...)
+ return &ast.Ident{
+ NamePos: pos,
+ Name: "C." + cgoName,
}
+ }
+ return typeExpr
+ } else {
+ var cgoName string
+ switch C.tinygo_clang_getCursorKind(cursor) {
+ case C.CXCursor_StructDecl:
+ cgoName = "struct_" + name
+ case C.CXCursor_UnionDecl:
+ cgoName = "union_" + name
+ default:
+ panic("unknown record declaration")
+ }
+ if _, ok := p.elaboratedTypes[cgoName]; !ok {
+ p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
+ typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
- typeExpr: &ast.StructType{
- Struct: pos,
- Fields: fieldList,
- },
- pos: pos,
+ typeExpr: typeExpr,
+ pos: pos,
+ bitfields: bitfieldList,
}
- default:
- panic("unreachable")
}
- }
- return &ast.Ident{
- NamePos: pos,
- Name: "C." + cgoName,
+ return &ast.Ident{
+ NamePos: pos,
+ Name: "C." + cgoName,
+ }
}
case C.CXType_Enum:
cursor := C.tinygo_clang_getTypeDeclaration(typ)
@@ -644,6 +600,76 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
}
}
+// makeASTRecordType parses a C record (struct or union) and translates it into
+// a Go struct type. Unions are implemented by setting the first field to a
+// zero-lengt "C union" field, which cannot be written in Go directly.
+func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) (*ast.StructType, []bitfieldInfo) {
+ fieldList := &ast.FieldList{
+ Opening: pos,
+ Closing: pos,
+ }
+ var bitfieldList []bitfieldInfo
+ inBitfield := false
+ bitfieldNum := 0
+ ref := storedRefs.Put(struct {
+ fieldList *ast.FieldList
+ pkg *cgoPackage
+ inBitfield *bool
+ bitfieldNum *int
+ bitfieldList *[]bitfieldInfo
+ }{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
+ defer storedRefs.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:
+ return &ast.StructType{
+ Struct: pos,
+ Fields: fieldList,
+ }, bitfieldList
+ case C.CXCursor_UnionDecl:
+ if bitfieldList != nil {
+ // This is valid C... but please don't do this.
+ p.errors = append(p.errors, scanner.Error{
+ Pos: p.fset.PositionFor(pos, true),
+ Msg: fmt.Sprintf("bitfield in a union is not supported"),
+ })
+ }
+ 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...)
+ }
+ return &ast.StructType{
+ Struct: pos,
+ Fields: fieldList,
+ }, bitfieldList
+ default:
+ panic("unknown record declaration")
+ }
+}
+
//export tinygo_clang_struct_visitor
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {