From 6108ee6859ff6fa2154ee588709e1e0b68f6b2b5 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 7 Nov 2019 15:34:07 +0100 Subject: [PATCH] cgo: refactor union support Instead of putting the magic in the AST, generate regular accessor methods. This avoids a number of special cases in the compiler, and avoids missing any of them. The resulting union accesses are somewhat clunkier to use, but the compiler implementation has far less coupling between the CGo implementation and the IR generator. --- cgo/cgo.go | 206 +++++++++++++++++++++++++++++++++++++- cgo/libclang.go | 123 +++++++++++------------ cgo/testdata/types.go | 37 ++++++- cgo/testdata/types.out.go | 31 ++++-- compiler/compiler.go | 57 +---------- compiler/interface.go | 4 - compiler/sizes.go | 35 ++----- testdata/cgo/main.go | 21 ++-- 8 files changed, 340 insertions(+), 174 deletions(-) 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 }