diff --git a/cgo/cgo.go b/cgo/cgo.go index e11b15ce..f20ea0d5 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -12,6 +12,7 @@ package cgo // source file parsing. import ( + "fmt" "go/ast" "go/token" "sort" @@ -69,9 +70,11 @@ type typedefInfo struct { // elaboratedTypeInfo contains some information about an elaborated type // (struct, union) found in the C AST. type elaboratedTypeInfo struct { - typeExpr *ast.StructType - pos token.Pos - bitfields []bitfieldInfo + typeExpr *ast.StructType + pos token.Pos + bitfields []bitfieldInfo + unionSize int64 // union size in bytes, nonzero when union getters/setters should be created + unionAlign int64 // union alignment in bytes } // bitfieldInfo contains information about a single bitfield in a struct. It @@ -608,13 +611,31 @@ func (p *cgoPackage) addElaboratedTypes() { Kind: ast.Typ, Name: typeName, } + typeExpr := typ.typeExpr + if typ.unionSize != 0 { + // Create getters/setters. + for _, field := range typ.typeExpr.Fields.List { + if len(field.Names) != 1 { + p.addError(typ.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names))) + continue + } + p.createUnionAccessor(field, typeName) + } + // Convert to a single-field struct type. + typeExpr = p.makeUnionField(typ) + if typeExpr == nil { + // There was an error, that was already added to the list of + // errors. + continue + } + } typeSpec := &ast.TypeSpec{ Name: &ast.Ident{ NamePos: typ.pos, Name: typeName, Obj: obj, }, - Type: typ.typeExpr, + Type: typeExpr, } obj.Decl = typeSpec gen.Specs = append(gen.Specs, typeSpec) @@ -627,6 +648,183 @@ func (p *cgoPackage) addElaboratedTypes() { } } +// makeUnionField creates a new struct from an existing *elaboratedTypeInfo, +// that has just a single field that must be accessed through special accessors. +// It returns nil when there is an error. In case of an error, that error has +// already been added to the list of errors using p.addError. +func (p *cgoPackage) makeUnionField(typ *elaboratedTypeInfo) *ast.StructType { + unionFieldTypeName, ok := map[int64]string{ + 1: "uint8", + 2: "uint16", + 4: "uint32", + 8: "uint64", + }[typ.unionAlign] + if !ok { + p.addError(typ.typeExpr.Struct, fmt.Sprintf("expected union alignment to be one of 1, 2, 4, or 8, but got %d", typ.unionAlign)) + return nil + } + var unionFieldType ast.Expr = &ast.Ident{ + NamePos: token.NoPos, + Name: unionFieldTypeName, + } + if typ.unionSize != typ.unionAlign { + // A plain struct{uintX} isn't enough, we have to make a + // struct{[N]uintX} to make the union big enough. + if typ.unionSize/typ.unionAlign*typ.unionAlign != typ.unionSize { + p.addError(typ.typeExpr.Struct, fmt.Sprintf("union alignment (%d) must be a multiple of union alignment (%d)", typ.unionSize, typ.unionAlign)) + return nil + } + unionFieldType = &ast.ArrayType{ + Len: &ast.BasicLit{ + Kind: token.INT, + Value: strconv.FormatInt(typ.unionSize/typ.unionAlign, 10), + }, + Elt: unionFieldType, + } + } + return &ast.StructType{ + Struct: typ.typeExpr.Struct, + Fields: &ast.FieldList{ + Opening: typ.typeExpr.Fields.Opening, + List: []*ast.Field{&ast.Field{ + Names: []*ast.Ident{ + &ast.Ident{ + NamePos: typ.typeExpr.Fields.Opening, + Name: "$union", + }, + }, + Type: unionFieldType, + }}, + Closing: typ.typeExpr.Fields.Closing, + }, + } +} + +// createUnionAccessor creates a function that returns a typed pointer to a +// union field for each field in a union. For example: +// +// func (union *C.union_1) unionfield_d() *float64 { +// return (*float64)(unsafe.Pointer(&union.$union)) +// } +// +// Where C.union_1 is defined as: +// +// type C.union_1 struct{ +// $union uint64 +// } +// +// The returned pointer can be used to get or set the field, or get the pointer +// to a subfield. +func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) { + if len(field.Names) != 1 { + panic("number of names in union field must be exactly 1") + } + fieldName := field.Names[0] + pos := fieldName.NamePos + + // The method receiver. + receiver := &ast.SelectorExpr{ + X: &ast.Ident{ + NamePos: pos, + Name: "union", + Obj: nil, + }, + Sel: &ast.Ident{ + NamePos: pos, + Name: "$union", + }, + } + + // Get the address of the $union field. + receiverPtr := &ast.UnaryExpr{ + Op: token.AND, + X: receiver, + } + + // Cast to unsafe.Pointer. + sourcePointer := &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{Name: "unsafe"}, + Sel: &ast.Ident{Name: "Pointer"}, + }, + Args: []ast.Expr{receiverPtr}, + } + + // Cast to the target pointer type. + targetPointer := &ast.CallExpr{ + Lparen: pos, + Fun: &ast.ParenExpr{ + Lparen: pos, + X: &ast.StarExpr{ + X: field.Type, + }, + Rparen: pos, + }, + Args: []ast.Expr{sourcePointer}, + Rparen: pos, + } + + // Create the accessor function. + accessor := &ast.FuncDecl{ + Recv: &ast.FieldList{ + Opening: pos, + List: []*ast.Field{ + &ast.Field{ + Names: []*ast.Ident{ + &ast.Ident{ + NamePos: pos, + Name: "union", + }, + }, + Type: &ast.StarExpr{ + Star: pos, + X: &ast.Ident{ + NamePos: pos, + Name: typeName, + Obj: nil, + }, + }, + }, + }, + Closing: pos, + }, + Name: &ast.Ident{ + NamePos: pos, + Name: "unionfield_" + fieldName.Name, + }, + Type: &ast.FuncType{ + Func: pos, + Params: &ast.FieldList{ + Opening: pos, + Closing: pos, + }, + Results: &ast.FieldList{ + List: []*ast.Field{ + &ast.Field{ + Type: &ast.StarExpr{ + Star: pos, + X: field.Type, + }, + }, + }, + }, + }, + Body: &ast.BlockStmt{ + Lbrace: pos, + List: []ast.Stmt{ + &ast.ReturnStmt{ + Return: pos, + Results: []ast.Expr{ + targetPointer, + }, + }, + }, + Rbrace: pos, + }, + } + p.generated.Decls = append(p.generated.Decls, accessor) +} + // createBitfieldGetter creates a bitfield getter function like the following: // // func (s *C.struct_foo) bitfield_b() byte { diff --git a/cgo/libclang.go b/cgo/libclang.go index 514e5326..d495384e 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -506,44 +506,37 @@ 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 cgoRecordPrefix string + switch C.tinygo_clang_getCursorKind(cursor) { + case C.CXCursor_StructDecl: + cgoRecordPrefix = "struct_" + case C.CXCursor_UnionDecl: + cgoRecordPrefix = "union_" + default: + // makeASTRecordType will create an appropriate error. + cgoRecordPrefix = "record_" + } 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). + typeInfo := p.makeASTRecordType(cursor, pos) + if typeInfo.bitfields != nil || typeInfo.unionSize != 0 { + // This record is a union or is a struct with bitfields, so we + // have to declare it as a named type (for getters/setters to + // work). p.anonStructNum++ - cgoName := "struct_" + strconv.Itoa(p.anonStructNum) - p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{ - typeExpr: typeExpr, - pos: pos, - bitfields: bitfieldList, - } + cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum) + p.elaboratedTypes[cgoName] = typeInfo return &ast.Ident{ NamePos: pos, Name: "C." + cgoName, } } - return typeExpr + return typeInfo.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: - // makeASTRecordType will create an appropriate error. - cgoName = "record_" + name - } + cgoName := cgoRecordPrefix + name 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: typeExpr, - pos: pos, - bitfields: bitfieldList, - } + p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos) } return &ast.Ident{ NamePos: pos, @@ -591,9 +584,8 @@ 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) { +// a Go struct type. +func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo { fieldList := &ast.FieldList{ Opening: pos, Closing: pos, @@ -613,53 +605,50 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) (*ast renameFieldKeywords(fieldList) switch C.tinygo_clang_getCursorKind(cursor) { case C.CXCursor_StructDecl: - return &ast.StructType{ - Struct: pos, - Fields: fieldList, - }, bitfieldList + return &elaboratedTypeInfo{ + typeExpr: &ast.StructType{ + Struct: pos, + Fields: fieldList, + }, + pos: pos, + bitfields: bitfieldList, + } case C.CXCursor_UnionDecl: + typeInfo := &elaboratedTypeInfo{ + typeExpr: &ast.StructType{ + Struct: pos, + Fields: fieldList, + }, + pos: pos, + bitfields: bitfieldList, + } + if len(fieldList.List) <= 1 { + // Useless union, treat it as a regular struct. + return typeInfo + } if bitfieldList != nil { // This is valid C... but please don't do this. p.addError(pos, "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...) + typ := C.tinygo_clang_getCursorType(cursor) + alignInBytes := int64(C.clang_Type_getAlignOf(typ)) + sizeInBytes := int64(C.clang_Type_getSizeOf(typ)) + if sizeInBytes == 0 { + p.addError(pos, "zero-length union is not supported") } - return &ast.StructType{ - Struct: pos, - Fields: fieldList, - }, bitfieldList + typeInfo.unionSize = sizeInBytes + typeInfo.unionAlign = alignInBytes + return typeInfo default: cursorKind := C.tinygo_clang_getCursorKind(cursor) cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind)) p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling)) - return &ast.StructType{ - Struct: pos, - }, nil + return &elaboratedTypeInfo{ + typeExpr: &ast.StructType{ + Struct: pos, + }, + pos: pos, + } } } diff --git a/cgo/testdata/types.go b/cgo/testdata/types.go index 08d8bb0e..0c8668cb 100644 --- a/cgo/testdata/types.go +++ b/cgo/testdata/types.go @@ -27,6 +27,25 @@ struct type2 { int _type; }; +// Unions. +typedef union { + // Union should be treated as a struct. + int i; +} union1_t; +typedef union { + // Union must contain a single field and have special getters/setters. + int i; + double d; + short s; +} union3_t; +typedef union union2d { + int i; + double d[2]; +} union2d_t; +typedef union { + unsigned char arr[10]; +} unionarrary_t; + // Enums. These define constant numbers. All these constants must be given the // correct number. typedef enum option { @@ -85,6 +104,12 @@ var ( _ C.struct_type1 _ C.struct_type2 + // Unions. + _ C.union1_t + _ C.union3_t + _ C.union2d_t + _ C.unionarrary_t + // Enums (anonymous and named). _ C.option_t _ C.enum_option @@ -98,7 +123,7 @@ var ( ) // Test bitfield accesses. -func foo() { +func accessBitfields() { var x C.bitfield_t x.start = 3 x.set_bitfield_a(4) @@ -108,3 +133,13 @@ func foo() { x.e = 5 var _ C.uchar = x.bitfield_a() } + +// Test union accesses. +func accessUnion() { + var union1 C.union1_t + union1.i = 5 + + var union2d C.union2d_t + var _ *C.int = union2d.unionfield_i() + var _ *[2]float64 = union2d.unionfield_d() +} diff --git a/cgo/testdata/types.out.go b/cgo/testdata/types.out.go index 0f632b88..d0fcc5a2 100644 --- a/cgo/testdata/types.out.go +++ b/cgo/testdata/types.out.go @@ -34,7 +34,7 @@ type C.uint uint32 type C.ulong uint32 type C.ulonglong uint64 type C.ushort uint16 -type C.bitfield_t = C.struct_1 +type C.bitfield_t = C.struct_2 type C.myIntArray = [10]C.int type C.myint = C.int type C.option2_t = C.uint @@ -49,21 +49,25 @@ type C.types_t = struct { d float64 ptr *C.int } +type C.union1_t = struct{ i C.int } +type C.union2d_t = C.union_union2d +type C.union3_t = C.union_1 +type C.unionarrary_t = struct{ arr [10]C.uchar } -func (s *C.struct_1) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } -func (s *C.struct_1) set_bitfield_a(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 } -func (s *C.struct_1) bitfield_b() C.uchar { +func (s *C.struct_2) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } +func (s *C.struct_2) set_bitfield_a(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 } +func (s *C.struct_2) bitfield_b() C.uchar { return s.__bitfield_1 >> 5 & 0x1 } -func (s *C.struct_1) set_bitfield_b(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 } -func (s *C.struct_1) bitfield_c() C.uchar { +func (s *C.struct_2) set_bitfield_b(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 } +func (s *C.struct_2) bitfield_c() C.uchar { return s.__bitfield_1 >> 6 } -func (s *C.struct_1) set_bitfield_c(value C.uchar, +func (s *C.struct_2) set_bitfield_c(value C.uchar, ) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 } -type C.struct_1 struct { +type C.struct_2 struct { start C.uchar __bitfield_1 C.uchar @@ -81,5 +85,16 @@ type C.struct_type1 struct { ___type C.int } type C.struct_type2 struct{ _type C.int } + +func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) } +func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) } +func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) } + +type C.union_1 struct{ $union uint64 } + +func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) } +func (union *C.union_union2d) unionfield_d() *[2]float64 { return (*[2]float64)(unsafe.Pointer(&union.$union)) } + +type C.union_union2d struct{ $union [2]uint64 } type C.enum_option C.int type C.enum_unused C.uint diff --git a/compiler/compiler.go b/compiler/compiler.go index a6effa4a..625ef28f 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -493,33 +493,6 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type { for i := 0; i < typ.NumFields(); i++ { members[i] = c.getLLVMType(typ.Field(i).Type()) } - if len(members) > 2 && typ.Field(0).Name() == "C union" { - // Not a normal struct but a C union emitted by cgo. - // Such a field name cannot be entered in regular Go code, this must - // be manually inserted in the AST so this is safe. - maxAlign := 0 - maxSize := uint64(0) - mainType := members[0] - for _, member := range members { - align := c.targetData.ABITypeAlignment(member) - size := c.targetData.TypeAllocSize(member) - if align > maxAlign { - maxAlign = align - mainType = member - } else if align == maxAlign && size > maxSize { - maxAlign = align - maxSize = size - mainType = member - } else if size > maxSize { - maxSize = size - } - } - members = []llvm.Type{mainType} - mainTypeSize := c.targetData.TypeAllocSize(mainType) - if mainTypeSize < maxSize { - members = append(members, llvm.ArrayType(c.ctx.Int8Type(), int(maxSize-mainTypeSize))) - } - } return c.ctx.StructType(members, false) case *types.Tuple: members := make([]llvm.Type, typ.Len()) @@ -1478,18 +1451,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { return c.builder.CreateExtractValue(value, expr.Index, ""), nil case *ssa.Field: value := c.getValue(frame, expr.X) - if s := expr.X.Type().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" { - // Extract a field from a CGo union. - // This could be done directly, but as this is a very infrequent - // operation it's much easier to bitcast it through an alloca. - resultType := c.getLLVMType(expr.Type()) - alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(value.Type(), "union.alloca") - c.builder.CreateStore(value, alloca) - bitcast := c.builder.CreateBitCast(alloca, llvm.PointerType(resultType, 0), "union.bitcast") - result := c.builder.CreateLoad(bitcast, "union.result") - c.emitLifetimeEnd(allocaPtr, allocaSize) - return result, nil - } result := c.builder.CreateExtractValue(value, expr.Field, "") return result, nil case *ssa.FieldAddr: @@ -1499,20 +1460,12 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { // > pointer of type *T to x. [...] If the evaluation of x would cause a // > run-time panic, then the evaluation of &x does too. c.emitNilCheck(frame, val, "gep") - if s := expr.X.Type().(*types.Pointer).Elem().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" { - // This is not a regular struct but actually an union. - // That simplifies things, as we can just bitcast the pointer to the - // right type. - ptrType := c.getLLVMType(expr.Type()) - return c.builder.CreateBitCast(val, ptrType, ""), nil - } else { - // Do a GEP on the pointer to get the field address. - indices := []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false), - } - return c.builder.CreateInBoundsGEP(val, indices, ""), nil + // Do a GEP on the pointer to get the field address. + indices := []llvm.Value{ + llvm.ConstInt(c.ctx.Int32Type(), 0, false), + llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false), } + return c.builder.CreateInBoundsGEP(val, indices, ""), nil case *ssa.Function: panic("function is not an expression") case *ssa.Global: diff --git a/compiler/interface.go b/compiler/interface.go index 3eef9394..25b62163 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -210,10 +210,6 @@ func getTypeCodeName(t types.Type) string { return "slice:" + getTypeCodeName(t.Elem()) case *types.Struct: elems := make([]string, t.NumFields()) - if t.NumFields() > 2 && t.Field(0).Name() == "C union" { - // TODO: report this as a normal error instead of panicking. - panic("cgo unions are not allowed in interfaces") - } for i := 0; i < t.NumFields(); i++ { embedded := "" if t.Field(i).Embedded() { diff --git a/compiler/sizes.go b/compiler/sizes.go index 6d212da0..7f2b87d3 100644 --- a/compiler/sizes.go +++ b/compiler/sizes.go @@ -63,12 +63,6 @@ func (s *StdSizes) Alignof(T types.Type) int64 { func (s *StdSizes) Offsetsof(fields []*types.Var) []int64 { offsets := make([]int64, len(fields)) - if len(fields) > 1 && fields[0].Name() == "C union" { - // This struct contains the magic "C union" field which indicates that - // this is actually a union from CGo. - // All fields in the union start at 0 so return that. - return offsets // all fields are still set to 0 - } var o int64 for i, f := range fields { a := s.Alignof(f.Type()) @@ -143,29 +137,12 @@ func (s *StdSizes) Sizeof(T types.Type) int64 { maxAlign = al } } - if fields[0].Name() == "C union" { - // Magic field that indicates this is a CGo union and not a struct. - // The size is the biggest element, aligned to the element with the - // biggest alignment. This is not necessarily the same, for example - // in the following union: - // union { int32_t l; int16_t s[3] } - maxSize := int64(0) - for _, field := range fields[1:] { - si := s.Sizeof(field.Type()) - if si > maxSize { - maxSize = si - } - } - return align(maxSize, maxAlign) - } else { - // This is a regular struct. - // Pick the size that fits this struct and add some alignment. Some - // structs have some extra padding at the end which should also be - // taken care of: - // struct { int32 n; byte b } - offsets := s.Offsetsof(fields) - return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign) - } + // Pick the size that fits this struct and add some alignment. Some + // structs have some extra padding at the end which should also be taken + // care of: + // struct { int32 n; byte b } + offsets := s.Offsetsof(fields) + return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign) case *types.Interface: return s.PtrSize * 2 case *types.Pointer: diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index 2e5222c1..9a111243 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -57,12 +57,14 @@ func main() { println("array:", C.globalArray[0], C.globalArray[1], C.globalArray[2]) println("union:", C.int(unsafe.Sizeof(C.globalUnion)) == C.globalUnionSize) C.unionSetShort(22) - println("union s:", C.globalUnion.s) + println("union s:", *C.globalUnion.unionfield_s()) C.unionSetFloat(3.14) - println("union f:", C.globalUnion.f) + println("union f:", *C.globalUnion.unionfield_f()) 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) + data := C.globalUnion.unionfield_data() + println("union global data:", data[0], data[1], data[2]) + returnedUnion := printUnion(C.globalUnion) + println("union field:", *returnedUnion.unionfield_f()) var _ C.union_joined = C.globalUnion printBitfield(&C.globalBitfield) C.globalBitfield.set_bitfield_a(7) @@ -105,11 +107,12 @@ 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:", union.data[0] == -33) - union.f = 6.28 - println("union f:", union.f) + data := union.unionfield_data() + println("union local data: ", data[0], data[1], data[2]) + *union.unionfield_s() = -33 + println("union s:", data[0] == -33) + *union.unionfield_f() = 6.28 + println("union f:", *union.unionfield_f()) return union }