diff --git a/loader/cgo.go b/loader/cgo.go index 46823c26..c88f61c4 100644 --- a/loader/cgo.go +++ b/loader/cgo.go @@ -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, diff --git a/loader/libclang.go b/loader/libclang.go index 401b3f08..a69bf993 100644 --- a/loader/libclang.go +++ b/loader/libclang.go @@ -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 diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index 07eb76e3..3e444f4b 100644 --- a/testdata/cgo/main.go +++ b/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 diff --git a/testdata/cgo/main.h b/testdata/cgo/main.h index 7eeb2c33..117f907a 100644 --- a/testdata/cgo/main.h +++ b/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; diff --git a/testdata/cgo/out.txt b/testdata/cgo/out.txt index 9b193c5d..ee34f0bd 100644 --- a/testdata/cgo/out.txt +++ b/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