diff options
author | Ayke van Laethem <[email protected]> | 2022-02-20 19:54:52 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2022-05-06 17:22:22 +0200 |
commit | 5afb63df60427bd5ddf7adb8e2d43258c2544ce6 (patch) | |
tree | c499ed27941c908fd1ac55c706eb2561e00964fc /cgo | |
parent | 1d2c39c3b9e46866161fe18f099d8ac5ab2a6425 (diff) | |
download | tinygo-5afb63df60427bd5ddf7adb8e2d43258c2544ce6.tar.gz tinygo-5afb63df60427bd5ddf7adb8e2d43258c2544ce6.zip |
cgo: refactor
This is a large refactor of the cgo package. It should fix a number of
smaller problems and be a bit more strict (like upstream CGo): it for
example requires every Go file in a package to include the header files
it needs instead of piggybacking on imports in earlier files.
The main benefit is that it should be a bit more maintainable and easier
to add new features in the future (like static functions).
This breaks the tinygo.org/x/bluetooth package, which should be updated
before this change lands.
Diffstat (limited to 'cgo')
-rw-r--r-- | cgo/cgo.go | 896 | ||||
-rw-r--r-- | cgo/libclang.go | 582 | ||||
-rw-r--r-- | cgo/testdata/basic.out.go | 33 | ||||
-rw-r--r-- | cgo/testdata/const.out.go | 37 | ||||
-rw-r--r-- | cgo/testdata/errors.go | 2 | ||||
-rw-r--r-- | cgo/testdata/errors.out.go | 42 | ||||
-rw-r--r-- | cgo/testdata/flags.out.go | 35 | ||||
-rw-r--r-- | cgo/testdata/symbols.out.go | 41 | ||||
-rw-r--r-- | cgo/testdata/types.out.go | 184 |
9 files changed, 916 insertions, 936 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go index 15b388694..42914a11a 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -18,7 +18,6 @@ import ( "go/scanner" "go/token" "path/filepath" - "sort" "strconv" "strings" @@ -35,45 +34,19 @@ type cgoPackage struct { packageDir string // full path to the package to process fset *token.FileSet tokenFiles map[string]*token.File - missingSymbols map[string]struct{} - constants map[string]constantInfo - functions map[string]*functionInfo - globals map[string]globalInfo - typedefs map[string]*typedefInfo - elaboratedTypes map[string]*elaboratedTypeInfo - enums map[string]enumInfo - anonStructNum int + definedGlobally map[string]ast.Node + anonDecls map[interface{}]string cflags []string // CFlags from #cgo lines ldflags []string // LDFlags from #cgo lines visitedFiles map[string][]byte } -// constantInfo stores some information about a CGo constant found by libclang -// and declared in the Go AST. -type constantInfo struct { - expr ast.Expr - pos token.Pos -} - -// functionInfo stores some information about a CGo function found by libclang -// and declared in the AST. -type functionInfo struct { - args []paramInfo - results *ast.FieldList - pos token.Pos - variadic bool -} - -// paramInfo is a parameter of a CGo function (see functionInfo). -type paramInfo struct { - name string - typeExpr ast.Expr -} - -// typedefInfo contains information about a single typedef in C. -type typedefInfo struct { - typeExpr ast.Expr - pos token.Pos +// cgoFile holds information only for a single Go file (with one or more +// `import "C"` statements). +type cgoFile struct { + *cgoPackage + defined map[string]ast.Node + names map[string]clangCursor } // elaboratedTypeInfo contains some information about an elaborated type @@ -97,18 +70,6 @@ type bitfieldInfo struct { endBit int64 // may be 0 meaning "until the end of the field" } -// enumInfo contains information about an enum in the C. -type enumInfo struct { - typeExpr ast.Expr - pos token.Pos -} - -// globalInfo contains information about a declared global variable in C. -type globalInfo struct { - typeExpr ast.Expr - pos token.Pos -} - // cgoAliases list type aliases between Go and C, for types that are equivalent // in both languages. See addTypeAliases. var cgoAliases = map[string]string{ @@ -125,24 +86,24 @@ var cgoAliases = map[string]string{ // builtinAliases are handled specially because they only exist on the Go side // of CGo, not on the CGo side (they're prefixed with "_Cgo_" there). -var builtinAliases = map[string]struct{}{ - "char": {}, - "schar": {}, - "uchar": {}, - "short": {}, - "ushort": {}, - "int": {}, - "uint": {}, - "long": {}, - "ulong": {}, - "longlong": {}, - "ulonglong": {}, +var builtinAliases = []string{ + "char", + "schar", + "uchar", + "short", + "ushort", + "int", + "uint", + "long", + "ulong", + "longlong", + "ulonglong", } -// cgoTypes lists some C types with ambiguous sizes that must be retrieved -// somehow from C. This is done by adding some typedefs to get the size of each -// type. -const cgoTypes = ` +// builtinAliasTypedefs lists some C types with ambiguous sizes that must be +// retrieved somehow from C. This is done by adding some typedefs to get the +// size of each type. +const builtinAliasTypedefs = ` # 1 "<cgo>" typedef char _Cgo_char; typedef signed char _Cgo_schar; @@ -202,13 +163,8 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string currentDir: dir, fset: fset, tokenFiles: map[string]*token.File{}, - missingSymbols: map[string]struct{}{}, - constants: map[string]constantInfo{}, - functions: map[string]*functionInfo{}, - globals: map[string]globalInfo{}, - typedefs: map[string]*typedefInfo{}, - elaboratedTypes: map[string]*elaboratedTypeInfo{}, - enums: map[string]enumInfo{}, + definedGlobally: map[string]ast.Node{}, + anonDecls: map[interface{}]string{}, visitedFiles: map[string][]byte{}, } @@ -238,7 +194,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string // This is always a bug in the cgo package. panic("unexpected error: " + err.Error()) } - // If the Comments field is not set to nil, the fmt package will get + // If the Comments field is not set to nil, the go/format package will get // confused about where comments should go. p.generated.Comments = nil // Adjust some of the functions in there. @@ -254,15 +210,10 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string } } // Patch some types, for example *C.char in C.CString. - astutil.Apply(p.generated, p.walker, nil) - - // Find all C.* symbols. - for _, f := range files { - astutil.Apply(f, p.findMissingCGoNames, nil) - } - for name := range builtinAliases { - p.missingSymbols["_Cgo_"+name] = struct{}{} - } + cf := p.newCGoFile() + astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool { + return cf.walker(cursor, nil) + }, nil) // Find `import "C"` C fragments in the file. cgoHeaders := make([]string, len(files)) // combined CGo header fragment for each file @@ -337,38 +288,33 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders) } + // Retrieve types such as C.int, C.longlong, etc from C. + p.newCGoFile().readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) { + gen := &ast.GenDecl{ + TokPos: token.NoPos, + Tok: token.TYPE, + } + for _, name := range builtinAliases { + typeSpec := p.getIntegerType("C."+name, names["_Cgo_"+name]) + gen.Specs = append(gen.Specs, typeSpec) + } + p.generated.Decls = append(p.generated.Decls, gen) + }) + // Process CGo imports for each file. for i, f := range files { - p.parseFragment(cgoHeaders[i]+cgoTypes, cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name())) - } - - // Declare functions found by libclang. - p.addFuncDecls() - - // Declare stub function pointer values found by libclang. - p.addFuncPtrDecls() - - // Declare globals found by libclang. - p.addConstDecls() - - // Declare globals found by libclang. - p.addVarDecls() - - // Forward C types to Go types (like C.uint32_t -> uint32). - p.addTypeAliases() - - // Add type declarations for C types, declared using typedef in C. - p.addTypedefs() - - // Add elaborated types for C structs and unions. - p.addElaboratedTypes() - - // Add enum types and enum constants for C enums. - p.addEnumTypes() - - // Patch the AST to use the declared types and functions. - for _, f := range files { - astutil.Apply(f, p.walker, nil) + cf := p.newCGoFile() + cf.readNames(cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) { + for _, name := range builtinAliases { + // Names such as C.int should not be obtained from C. + // This works around an issue in picolibc that has `#define int` + // in a header file. + delete(names, name) + } + astutil.Apply(f, func(cursor *astutil.Cursor) bool { + return cf.walker(cursor, names) + }, nil) + }) } // Print the newly generated in-memory AST, for debugging. @@ -377,6 +323,14 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors } +func (p *cgoPackage) newCGoFile() *cgoFile { + return &cgoFile{ + cgoPackage: p, + defined: make(map[string]ast.Node), + names: make(map[string]clangCursor), + } +} + // makePathsAbsolute converts some common path compiler flags (-I, -L) from // relative flags into absolute flags, if they are relative. This is necessary // because the C compiler is usually not invoked from the package path. @@ -484,365 +438,6 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin return text } -// addFuncDecls adds the C function declarations found by libclang in the -// comment above the `import "C"` statement. -func (p *cgoPackage) addFuncDecls() { - names := make([]string, 0, len(p.functions)) - for name := range p.functions { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - fn := p.functions[name] - obj := &ast.Object{ - Kind: ast.Fun, - Name: "C." + name, - } - args := make([]*ast.Field, len(fn.args)) - decl := &ast.FuncDecl{ - Doc: &ast.CommentGroup{ - List: []*ast.Comment{ - { - Slash: fn.pos - 1, - Text: "//export " + name, - }, - }, - }, - Name: &ast.Ident{ - NamePos: fn.pos, - Name: "C." + name, - Obj: obj, - }, - Type: &ast.FuncType{ - Func: fn.pos, - Params: &ast.FieldList{ - Opening: fn.pos, - List: args, - Closing: fn.pos, - }, - Results: fn.results, - }, - } - if fn.variadic { - decl.Doc.List = append(decl.Doc.List, &ast.Comment{ - Slash: fn.pos - 1, - Text: "//go:variadic", - }) - } - obj.Decl = decl - for i, arg := range fn.args { - args[i] = &ast.Field{ - Names: []*ast.Ident{ - { - NamePos: fn.pos, - Name: arg.name, - Obj: &ast.Object{ - Kind: ast.Var, - Name: arg.name, - Decl: decl, - }, - }, - }, - Type: arg.typeExpr, - } - } - p.generated.Decls = append(p.generated.Decls, decl) - } -} - -// addFuncPtrDecls creates stub declarations of function pointer values. These -// values will later be replaced with the real values in the compiler. -// It adds code like the following to the AST: -// -// var ( -// C.add unsafe.Pointer -// C.mul unsafe.Pointer -// // ... -// ) -func (p *cgoPackage) addFuncPtrDecls() { - if len(p.functions) == 0 { - return - } - names := make([]string, 0, len(p.functions)) - for name := range p.functions { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - gen := &ast.GenDecl{ - TokPos: token.NoPos, - Tok: token.VAR, - Lparen: token.NoPos, - Rparen: token.NoPos, - } - fn := p.functions[name] - obj := &ast.Object{ - Kind: ast.Typ, - Name: "C." + name + "$funcaddr", - } - valueSpec := &ast.ValueSpec{ - Names: []*ast.Ident{{ - NamePos: fn.pos, - Name: "C." + name + "$funcaddr", - Obj: obj, - }}, - Type: &ast.SelectorExpr{ - X: &ast.Ident{ - NamePos: fn.pos, - Name: "unsafe", - }, - Sel: &ast.Ident{ - NamePos: fn.pos, - Name: "Pointer", - }, - }, - } - obj.Decl = valueSpec - gen.Specs = append(gen.Specs, valueSpec) - p.generated.Decls = append(p.generated.Decls, gen) - } -} - -// addConstDecls declares external C constants in the Go source. -// It adds code like the following to the AST: -// -// const C.CONST_INT = 5 -// const C.CONST_FLOAT = 5.8 -// // ... -func (p *cgoPackage) addConstDecls() { - if len(p.constants) == 0 { - return - } - names := make([]string, 0, len(p.constants)) - for name := range p.constants { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - gen := &ast.GenDecl{ - TokPos: token.NoPos, - Tok: token.CONST, - Lparen: token.NoPos, - Rparen: token.NoPos, - } - constVal := p.constants[name] - obj := &ast.Object{ - Kind: ast.Con, - Name: "C." + name, - } - valueSpec := &ast.ValueSpec{ - Names: []*ast.Ident{{ - NamePos: constVal.pos, - Name: "C." + name, - Obj: obj, - }}, - Values: []ast.Expr{constVal.expr}, - } - obj.Decl = valueSpec - gen.Specs = append(gen.Specs, valueSpec) - p.generated.Decls = append(p.generated.Decls, gen) - } -} - -// addVarDecls declares external C globals in the Go source. -// It adds code like the following to the AST: -// -// var C.globalInt int -// var C.globalBool bool -// // ... -func (p *cgoPackage) addVarDecls() { - if len(p.globals) == 0 { - return - } - names := make([]string, 0, len(p.globals)) - for name := range p.globals { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - global := p.globals[name] - gen := &ast.GenDecl{ - TokPos: global.pos, - Tok: token.VAR, - Lparen: token.NoPos, - Rparen: token.NoPos, - Doc: &ast.CommentGroup{ - List: []*ast.Comment{ - { - Slash: global.pos - 1, - Text: "//go:extern " + name, - }, - }, - }, - } - obj := &ast.Object{ - Kind: ast.Var, - Name: "C." + name, - } - valueSpec := &ast.ValueSpec{ - Names: []*ast.Ident{{ - NamePos: global.pos, - Name: "C." + name, - Obj: obj, - }}, - Type: global.typeExpr, - } - obj.Decl = valueSpec - gen.Specs = append(gen.Specs, valueSpec) - p.generated.Decls = append(p.generated.Decls, gen) - } -} - -// addTypeAliases aliases some built-in Go types with their equivalent C types. -// It adds code like the following to the AST: -// -// type C.int8_t = int8 -// type C.int16_t = int16 -// // ... -func (p *cgoPackage) addTypeAliases() { - aliasKeys := make([]string, 0, len(cgoAliases)) - for key := range cgoAliases { - aliasKeys = append(aliasKeys, key) - } - sort.Strings(aliasKeys) - for _, typeName := range aliasKeys { - gen := &ast.GenDecl{ - TokPos: token.NoPos, - Tok: token.TYPE, - Lparen: token.NoPos, - Rparen: token.NoPos, - } - goTypeName := cgoAliases[typeName] - obj := &ast.Object{ - Kind: ast.Typ, - Name: typeName, - } - typeSpec := &ast.TypeSpec{ - Name: &ast.Ident{ - NamePos: token.NoPos, - Name: typeName, - Obj: obj, - }, - Assign: p.generatedPos, - Type: &ast.Ident{ - NamePos: token.NoPos, - Name: goTypeName, - }, - } - obj.Decl = typeSpec - gen.Specs = append(gen.Specs, typeSpec) - p.generated.Decls = append(p.generated.Decls, gen) - } -} - -func (p *cgoPackage) addTypedefs() { - if len(p.typedefs) == 0 { - return - } - names := make([]string, 0, len(p.typedefs)) - for name := range p.typedefs { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - gen := &ast.GenDecl{ - TokPos: token.NoPos, - Tok: token.TYPE, - } - typedef := p.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). - continue - } - obj := &ast.Object{ - Kind: ast.Typ, - Name: typeName, - } - typeSpec := &ast.TypeSpec{ - Name: &ast.Ident{ - NamePos: typedef.pos, - Name: typeName, - Obj: obj, - }, - Type: typedef.typeExpr, - } - if isAlias { - typeSpec.Assign = typedef.pos - } - obj.Decl = typeSpec - gen.Specs = append(gen.Specs, typeSpec) - p.generated.Decls = append(p.generated.Decls, gen) - } -} - -// addElaboratedTypes adds C elaborated types as aliases. These are the "struct -// foo" or "union foo" types, often used in a typedef. -// -// See also: -// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier -func (p *cgoPackage) addElaboratedTypes() { - if len(p.elaboratedTypes) == 0 { - return - } - names := make([]string, 0, len(p.elaboratedTypes)) - for name := range p.elaboratedTypes { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - gen := &ast.GenDecl{ - TokPos: token.NoPos, - Tok: token.TYPE, - } - typ := p.elaboratedTypes[name] - typeName := "C." + name - obj := &ast.Object{ - 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: typeExpr, - } - obj.Decl = typeSpec - gen.Specs = append(gen.Specs, typeSpec) - // If this struct has bitfields, create getters for them. - for _, bitfield := range typ.bitfields { - p.createBitfieldGetter(bitfield, typeName) - p.createBitfieldSetter(bitfield, typeName) - } - p.generated.Decls = append(p.generated.Decls, gen) - } -} - // 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 @@ -1304,80 +899,319 @@ func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string p.generated.Decls = append(p.generated.Decls, setter) } -// addEnumTypes adds C enums to the AST. For example, the following C code: -// -// enum option { -// optionA, -// optionB = 5, -// }; -// -// is translated to the following Go code equivalent: -// -// type C.enum_option int32 -// -// The constants are treated just like macros so are inserted into the AST by -// addConstDecls. -// See also: https://en.cppreference.com/w/c/language/enum -func (p *cgoPackage) addEnumTypes() { - if len(p.enums) == 0 { - return +// isEquivalentAST returns whether the given two AST nodes are equivalent as far +// as CGo is concerned. This is used to check that C types, globals, etc defined +// in different CGo header snippets are actually the same type (and probably +// even defined in the same header file, just in different translation units). +func (p *cgoPackage) isEquivalentAST(a, b ast.Node) bool { + switch node := a.(type) { + case *ast.ArrayType: + b, ok := b.(*ast.ArrayType) + if !ok { + return false + } + if !p.isEquivalentAST(node.Len, b.Len) { + return false + } + return p.isEquivalentAST(node.Elt, b.Elt) + case *ast.BasicLit: + b, ok := b.(*ast.BasicLit) + if !ok { + return false + } + // Note: this comparison is not correct in general ("1e2" equals "100"), + // but is correct for its use in the cgo package. + return node.Value == b.Value + case *ast.CommentGroup: + b, ok := b.(*ast.CommentGroup) + if !ok { + return false + } + if len(node.List) != len(b.List) { + return false + } + for i, c := range node.List { + if c.Text != b.List[i].Text { + return false + } + } + return true + case *ast.FieldList: + b, ok := b.(*ast.FieldList) + if !ok { + return false + } + if len(node.List) != len(b.List) { + return false + } + for i, f := range node.List { + if !p.isEquivalentAST(f, b.List[i]) { + return false + } + } + return true + case *ast.Field: + b, ok := b.(*ast.Field) + if !ok { + return false + } + if !p.isEquivalentAST(node.Type, b.Type) { + return false + } + if len(node.Names) != len(b.Names) { + return false + } + for i, name := range node.Names { + if name.Name != b.Names[i].Name { + return false + } + } + return true + case *ast.FuncDecl: + b, ok := b.(*ast.FuncDecl) + if !ok { + return false + } + if node.Name.Name != b.Name.Name { + return false + } + if node.Doc != b.Doc { + if !p.isEquivalentAST(node.Doc, b.Doc) { + return false + } + } + if node.Recv != b.Recv { + if !p.isEquivalentAST(node.Recv, b.Recv) { + return false + } + } + if !p.isEquivalentAST(node.Type.Params, b.Type.Params) { + return false + } + return p.isEquivalentAST(node.Type.Results, b.Type.Results) + case *ast.GenDecl: + b, ok := b.(*ast.GenDecl) + if !ok { + return false + } + if node.Doc != b.Doc { + if !p.isEquivalentAST(node.Doc, b.Doc) { + return false + } + } + if len(node.Specs) != len(b.Specs) { + return false + } + for i, s := range node.Specs { + if !p.isEquivalentAST(s, b.Specs[i]) { + return false + } + } + return true + case *ast.Ident: + b, ok := b.(*ast.Ident) + if !ok { + return false + } + return node.Name == b.Name + case *ast.SelectorExpr: + b, ok := b.(*ast.SelectorExpr) + if !ok { + return false + } + if !p.isEquivalentAST(node.Sel, b.Sel) { + return false + } + return p.isEquivalentAST(node.X, b.X) + case *ast.StarExpr: + b, ok := b.(*ast.StarExpr) + if !ok { + return false + } + return p.isEquivalentAST(node.X, b.X) + case *ast.StructType: + b, ok := b.(*ast.StructType) + if !ok { + return false + } + return p.isEquivalentAST(node.Fields, b.Fields) + case *ast.TypeSpec: + b, ok := b.(*ast.TypeSpec) + if !ok { + return false + } + if node.Name.Name != b.Name.Name { + return false + } + if node.Assign.IsValid() != b.Assign.IsValid() { + return false + } + return p.isEquivalentAST(node.Type, b.Type) + case *ast.ValueSpec: + b, ok := b.(*ast.ValueSpec) + if !ok { + return false + } + if len(node.Names) != len(b.Names) { + return false + } + for i, name := range node.Names { + if name.Name != b.Names[i].Name { + return false + } + } + if node.Type != b.Type && !p.isEquivalentAST(node.Type, b.Type) { + return false + } + if len(node.Values) != len(b.Values) { + return false + } + for i, value := range node.Values { + if !p.isEquivalentAST(value, b.Values[i]) { + return false + } + } + return true + case nil: + p.addError(token.NoPos, "internal error: AST node is nil") + return true + default: + p.addError(a.Pos(), fmt.Sprintf("internal error: unknown AST node: %T", a)) + return true } - names := make([]string, 0, len(p.enums)) - for name := range p.enums { - names = append(names, name) +} + +// getPos returns node.Pos(), and tries to obtain a closely related position if +// that fails. +func getPos(node ast.Node) token.Pos { + pos := node.Pos() + if pos.IsValid() { + return pos } - sort.Strings(names) - for _, name := range names { - gen := &ast.GenDecl{ - TokPos: token.NoPos, - Tok: token.TYPE, + if decl, ok := node.(*ast.GenDecl); ok { + // *ast.GenDecl often doesn't have TokPos defined, so look at the first + // spec. + return getPos(decl.Specs[0]) + } + return token.NoPos +} + +// getUnnamedDeclName creates a name (with the given prefix) for the given C +// declaration. This is used for structs, unions, and enums that are often +// defined without a name and used in a typedef. +func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string { + if name, ok := p.anonDecls[itf]; ok { + return name + } + name := prefix + strconv.Itoa(len(p.anonDecls)) + p.anonDecls[itf] = name + return name +} + +// getASTDeclName will declare the given C AST node (if not already defined) and +// will return its name, in the form of C.foo. +func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string { + // Some types are defined in stdint.h and map directly to a particular Go + // type. + if alias := cgoAliases["C."+name]; alias != "" { + return alias + } + node := f.getASTDeclNode(name, found, iscall) + if _, ok := node.(*ast.FuncDecl); ok && !iscall { + return "C." + name + "$funcaddr" + } + return "C." + name +} + +// getASTDeclNode will declare the given C AST node (if not already defined) and +// returns it. +func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) ast.Node { + if node, ok := f.defined[name]; ok { + // Declaration was found in the current file, so return it immediately. + return node + } + + if node, ok := f.definedGlobally[name]; ok { + // Declaration was previously created, but not for the current file. It + // may be different (because it comes from a different CGo snippet), so + // we need to check whether the AST for this definition is equivalent. + f.defined[name] = nil + newNode, _ := f.createASTNode(name, found) + if !f.isEquivalentAST(node, newNode) { + // It's not. Return a nice error with both locations. + // Original cgo reports an error like + // cgo: inconsistent definitions for C.myint + // which is far less helpful. + f.addError(getPos(node), "defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type") } - typ := p.enums[name] - typeName := "C.enum_" + name - obj := &ast.Object{ - Kind: ast.Typ, - Name: typeName, - } - typeSpec := &ast.TypeSpec{ - Name: &ast.Ident{ - NamePos: typ.pos, - Name: typeName, - Obj: obj, + f.defined[name] = node + return node + } + + // The declaration has no AST node. Create it now. + f.defined[name] = nil + node, elaboratedType := f.createASTNode(name, found) + f.defined[name] = node + f.definedGlobally[name] = node + switch node := node.(type) { + case *ast.FuncDecl: + f.generated.Decls = append(f.generated.Decls, node) + // Also add a declaration like the following: + // var C.foo$funcaddr unsafe.Pointer + f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{{Name: "C." + name + "$funcaddr"}}, + Type: &ast.SelectorExpr{ + X: &ast.Ident{Name: "unsafe"}, + Sel: &ast.Ident{Name: "Pointer"}, + }, + }, }, - Type: typ.typeExpr, - } - obj.Decl = typeSpec - gen.Specs = append(gen.Specs, typeSpec) - p.generated.Decls = append(p.generated.Decls, gen) + }) + case *ast.GenDecl: + f.generated.Decls = append(f.generated.Decls, node) + case *ast.TypeSpec: + f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{ + Tok: token.TYPE, + Specs: []ast.Spec{node}, + }) + case nil: + // Node may be nil in case of an error. In that case, just don't add it + // as a declaration. + default: + panic("unexpected AST node") } -} -// findMissingCGoNames traverses the AST and finds all C.something names. Only -// these symbols are extracted from the parsed C AST and converted to the Go -// equivalent. -func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool { - switch node := cursor.Node().(type) { - case *ast.SelectorExpr: - x, ok := node.X.(*ast.Ident) - if !ok { - return true + // If this is a struct or union it may need bitfields or union accessor + // methods. + if elaboratedType != nil { + // Add struct bitfields. + for _, bitfield := range elaboratedType.bitfields { + f.createBitfieldGetter(bitfield, "C."+name) + f.createBitfieldSetter(bitfield, "C."+name) } - if x.Name == "C" { - name := node.Sel.Name - if _, ok := builtinAliases[name]; ok { - name = "_Cgo_" + name + if elaboratedType.unionSize != 0 { + // Create union getters/setters. + for _, field := range elaboratedType.typeExpr.Fields.List { + if len(field.Names) != 1 { + f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names))) + continue + } + f.createUnionAccessor(field, "C."+name) } - p.missingSymbols[name] = struct{}{} } } - return true + + return node } // walker replaces all "C".<something> expressions to literal "C.<something>" // expressions. Such expressions are impossible to write in Go (a dot cannot be // used in the middle of a name) so in practice all C identifiers live in a // separate namespace (no _Cgo_ hacks like in gc). -func (p *cgoPackage) walker(cursor *astutil.Cursor) bool { +func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool { switch node := cursor.Node().(type) { case *ast.CallExpr: fun, ok := node.Fun.(*ast.SelectorExpr) @@ -1388,10 +1222,10 @@ func (p *cgoPackage) walker(cursor *astutil.Cursor) bool { if !ok { return true } - if _, ok := p.functions[fun.Sel.Name]; ok && x.Name == "C" { + if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" { node.Fun = &ast.Ident{ NamePos: x.NamePos, - Name: "C." + fun.Sel.Name, + Name: f.getASTDeclName(fun.Sel.Name, found, true), } } case *ast.SelectorExpr: @@ -1401,8 +1235,8 @@ func (p *cgoPackage) walker(cursor *astutil.Cursor) bool { } if x.Name == "C" { name := "C." + node.Sel.Name - if _, ok := p.functions[node.Sel.Name]; ok { - name += "$funcaddr" + if found, ok := names[node.Sel.Name]; ok { + name = f.getASTDeclName(node.Sel.Name, found, false) } cursor.Replace(&ast.Ident{ NamePos: x.NamePos, diff --git a/cgo/libclang.go b/cgo/libclang.go index f0663794f..f7aca9689 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -72,7 +72,11 @@ var diagnosticSeverity = [...]string{ C.CXDiagnostic_Fatal: "fatal", } -func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) { +// Alias so that cgo.go (which doesn't import Clang related stuff and is in +// theory decoupled from Clang) can also use this type. +type clangCursor = C.GoCXCursor + +func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) { index := C.clang_createIndex(0, 0) defer C.clang_disposeIndex(index) @@ -119,8 +123,8 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st spelling := getString(C.clang_getDiagnosticSpelling(diagnostic)) severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)] location := C.clang_getDiagnosticLocation(diagnostic) - pos := p.getClangLocationPosition(location, unit) - p.addError(pos, severity+": "+spelling) + pos := f.getClangLocationPosition(location, unit) + f.addError(pos, severity+": "+spelling) } for i := 0; i < numDiagnostics; i++ { diagnostic := C.clang_getDiagnostic(unit, C.uint(i)) @@ -135,7 +139,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st } // Extract information required by CGo. - ref := storedRefs.Put(p) + ref := storedRefs.Put(f) defer storedRefs.Remove(ref) cursor := C.tinygo_clang_getTranslationUnitCursor(unit) C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref)) @@ -155,35 +159,64 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename st data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size] // Hash the contents if it isn't hashed yet. - if _, ok := p.visitedFiles[path]; !ok { + if _, ok := f.visitedFiles[path]; !ok { // already stored sum := sha512.Sum512_224(data) - p.visitedFiles[path] = sum[:] + f.visitedFiles[path] = sum[:] } } inclusionCallbackRef := storedRefs.Put(inclusionCallback) defer storedRefs.Remove(inclusionCallbackRef) C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef)) + + // Do all the C AST operations inside a callback. This makes sure that + // libclang related memory is only freed after it is not necessary anymore. + callback(f.names) } -//export tinygo_clang_globals_visitor -func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { - p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage) +// Convert the AST node under the given Clang cursor to a Go AST node and return +// it. +func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) { kind := C.tinygo_clang_getCursorKind(c) - pos := p.getCursorPosition(c) + pos := f.getCursorPosition(c) switch kind { case C.CXCursor_FunctionDecl: - name := getString(C.tinygo_clang_getCursorSpelling(c)) - if _, required := p.missingSymbols[name]; !required { - return C.CXChildVisit_Continue - } cursorType := C.tinygo_clang_getCursorType(c) numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) - fn := &functionInfo{ - pos: pos, - variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0, + obj := &ast.Object{ + Kind: ast.Fun, + Name: "C." + name, + } + args := make([]*ast.Field, numArgs) + decl := &ast.FuncDecl{ + Doc: &ast.CommentGroup{ + List: []*ast.Comment{ + { + Slash: pos - 1, + Text: "//export " + name, + }, + }, + }, + Name: &ast.Ident{ + NamePos: pos, + Name: "C." + name, + Obj: obj, + }, + Type: &ast.FuncType{ + Func: pos, + Params: &ast.FieldList{ + Opening: pos, + List: args, + Closing: pos, + }, + }, + } + if C.clang_isFunctionTypeVariadic(cursorType) != 0 { + decl.Doc.List = append(decl.Doc.List, &ast.Comment{ + Slash: pos - 1, + Text: "//go:variadic", + }) } - p.functions[name] = fn for i := 0; i < numArgs; i++ { arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i)) argName := getString(C.tinygo_clang_getCursorSpelling(arg)) @@ -191,50 +224,108 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient if argName == "" { argName = "$" + strconv.Itoa(i) } - fn.args = append(fn.args, paramInfo{ - name: argName, - typeExpr: p.makeDecayingASTType(argType, pos), - }) + args[i] = &ast.Field{ + Names: []*ast.Ident{ + { + NamePos: pos, + Name: argName, + Obj: &ast.Object{ + Kind: ast.Var, + Name: argName, + Decl: decl, + }, + }, + }, + Type: f.makeDecayingASTType(argType, pos), + } } resultType := C.tinygo_clang_getCursorResultType(c) if resultType.kind != C.CXType_Void { - fn.results = &ast.FieldList{ + decl.Type.Results = &ast.FieldList{ List: []*ast.Field{ { - Type: p.makeASTType(resultType, pos), + Type: f.makeASTType(resultType, pos), }, }, } } - case C.CXCursor_StructDecl: - typ := C.tinygo_clang_getCursorType(c) - name := getString(C.tinygo_clang_getCursorSpelling(c)) - if _, required := p.missingSymbols["struct_"+name]; !required { - return C.CXChildVisit_Continue + obj.Decl = decl + return decl, nil + case C.CXCursor_StructDecl, C.CXCursor_UnionDecl: + typ := f.makeASTRecordType(c, pos) + typeName := "C." + name + typeExpr := typ.typeExpr + if typ.unionSize != 0 { + // Convert to a single-field struct type. + typeExpr = f.makeUnionField(typ) + } + obj := &ast.Object{ + Kind: ast.Typ, + Name: typeName, } - p.makeASTType(typ, pos) + typeSpec := &ast.TypeSpec{ + Name: &ast.Ident{ + NamePos: typ.pos, + Name: typeName, + Obj: obj, + }, + Type: typeExpr, + } + obj.Decl = typeSpec + return typeSpec, typ case C.CXCursor_TypedefDecl: - typedefType := C.tinygo_clang_getCursorType(c) - name := getString(C.clang_getTypedefName(typedefType)) - if _, required := p.missingSymbols[name]; !required { - return C.CXChildVisit_Continue + typeName := "C." + name + underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) + obj := &ast.Object{ + Kind: ast.Typ, + Name: typeName, } - p.makeASTType(typedefType, pos) - case C.CXCursor_VarDecl: - name := getString(C.tinygo_clang_getCursorSpelling(c)) - if _, required := p.missingSymbols[name]; !required { - return C.CXChildVisit_Continue + typeSpec := &ast.TypeSpec{ + Name: &ast.Ident{ + NamePos: pos, + Name: typeName, + Obj: obj, + }, + Type: f.makeASTType(underlyingType, pos), } + if underlyingType.kind != C.CXType_Enum { + typeSpec.Assign = pos + } + obj.Decl = typeSpec + return typeSpec, nil + case C.CXCursor_VarDecl: cursorType := C.tinygo_clang_getCursorType(c) - p.globals[name] = globalInfo{ - typeExpr: p.makeASTType(cursorType, pos), - pos: pos, + typeExpr := f.makeASTType(cursorType, pos) + gen := &ast.GenDecl{ + TokPos: pos, + Tok: token.VAR, + Lparen: token.NoPos, + Rparen: token.NoPos, + Doc: &ast.CommentGroup{ + List: []*ast.Comment{ + { + Slash: pos - 1, + Text: "//go:extern " + name, + }, + }, + }, } - case C.CXCursor_MacroDefinition: - name := getString(C.tinygo_clang_getCursorSpelling(c)) - if _, required := p.missingSymbols[name]; !required { - return C.CXChildVisit_Continue + obj := &ast.Object{ + Kind: ast.Var, + Name: "C." + name, } + valueSpec := &ast.ValueSpec{ + Names: []*ast.Ident{{ + NamePos: pos, + Name: "C." + name, + Obj: obj, + }}, + Type: typeExpr, + } + obj.Decl = valueSpec + gen.Specs = append(gen.Specs, valueSpec) + return gen, nil + case C.CXCursor_MacroDefinition: sourceRange := C.tinygo_clang_getCursorExtent(c) start := C.clang_getRangeStart(sourceRange) end := C.clang_getRangeEnd(sourceRange) @@ -242,17 +333,17 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient var startOffset, endOffset C.unsigned C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset) if file == nil { - p.addError(pos, "internal error: could not find file where macro is defined") - break + f.addError(pos, "internal error: could not find file where macro is defined") + return nil, nil } C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset) if file != endFile { - p.addError(pos, "internal error: expected start and end location of a macro to be in the same file") - break + f.addError(pos, "internal error: expected start and end location of a macro to be in the same file") + return nil, nil } if startOffset > endOffset { - p.addError(pos, "internal error: start offset of macro is after end offset") - break + f.addError(pos, "internal error: start offset of macro is after end offset") + return nil, nil } // read file contents and extract the relevant byte range @@ -260,31 +351,94 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient var size C.size_t sourcePtr := C.clang_getFileContents(tu, file, &size) if endOffset >= C.uint(size) { - p.addError(pos, "internal error: end offset of macro lies after end of file") - break + f.addError(pos, "internal error: end offset of macro lies after end of file") + return nil, nil } source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset]) if !strings.HasPrefix(source, name) { - p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source)) - break + f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source)) + return nil, nil } value := source[len(name):] // Try to convert this #define into a Go constant expression. - expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value) + expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value) if scannerError != nil { - p.errors = append(p.errors, *scannerError) + f.errors = append(f.errors, *scannerError) + return nil, nil + } + + gen := &ast.GenDecl{ + TokPos: token.NoPos, + Tok: token.CONST, + Lparen: token.NoPos, + Rparen: token.NoPos, } - if expr != nil { - // Parsing was successful. - p.constants[name] = constantInfo{expr, pos} + obj := &ast.Object{ + Kind: ast.Con, + Name: "C." + name, } + valueSpec := &ast.ValueSpec{ + Names: []*ast.Ident{{ + NamePos: pos, + Name: "C." + name, + Obj: obj, + }}, + Values: []ast.Expr{expr}, + } + obj.Decl = valueSpec + gen.Specs = append(gen.Specs, valueSpec) + return gen, nil case C.CXCursor_EnumDecl: - // Visit all enums, because the fields may be used even when the enum - // type itself is not. - typ := C.tinygo_clang_getCursorType(c) - p.makeASTType(typ, pos) + obj := &ast.Object{ + Kind: ast.Typ, + Name: "C." + name, + } + underlying := C.tinygo_clang_getEnumDeclIntegerType(c) + // TODO: gc's CGo implementation uses types such as `uint32` for enums + // instead of types such as C.int, which are used here. + typeSpec := &ast.TypeSpec{ + Name: &ast.Ident{ + NamePos: pos, + Name: "C." + name, + Obj: obj, + }, + Assign: pos, + Type: f.makeASTType(underlying, pos), + } + obj.Decl = typeSpec + return typeSpec, nil + case C.CXCursor_EnumConstantDecl: + value := C.tinygo_clang_getEnumConstantDeclValue(c) + expr := &ast.BasicLit{ + ValuePos: pos, + Kind: token.INT, + Value: strconv.FormatInt(int64(value), 10), + } + gen := &ast.GenDecl{ + TokPos: token.NoPos, + Tok: token.CONST, + Lparen: token.NoPos, + Rparen: token.NoPos, + } + obj := &ast.Object{ + Kind: ast.Con, + Name: "C." + name, + } + valueSpec := &ast.ValueSpec{ + Names: []*ast.Ident{{ + NamePos: pos, + Name: "C." + name, + Obj: obj, + }}, + Values: []ast.Expr{expr}, + } + obj.Decl = valueSpec + gen.Specs = append(gen.Specs, valueSpec) + return gen, nil + default: + f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind)) + return nil, nil } - return C.CXChildVisit_Continue } func getString(clangString C.CXString) (s string) { @@ -294,6 +448,49 @@ func getString(clangString C.CXString) (s string) { return } +//export tinygo_clang_globals_visitor +func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { + f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile) + switch C.tinygo_clang_getCursorKind(c) { + case C.CXCursor_FunctionDecl: + name := getString(C.tinygo_clang_getCursorSpelling(c)) + f.names[name] = c + case C.CXCursor_StructDecl: + name := getString(C.tinygo_clang_getCursorSpelling(c)) + if name != "" { + f.names["struct_"+name] = c + } + case C.CXCursor_UnionDecl: + name := getString(C.tinygo_clang_getCursorSpelling(c)) + if name != "" { + f.names["union_"+name] = c + } + case C.CXCursor_TypedefDecl: + typedefType := C.tinygo_clang_getCursorType(c) + name := getString(C.clang_getTypedefName(typedefType)) + f.names[name] = c + case C.CXCursor_VarDecl: + name := getString(C.tinygo_clang_getCursorSpelling(c)) + f.names[name] = c + case C.CXCursor_MacroDefinition: + name := getString(C.tinygo_clang_getCursorSpelling(c)) + f.names[name] = c + case C.CXCursor_EnumDecl: + name := getString(C.tinygo_clang_getCursorSpelling(c)) + if name != "" { + // Named enum, which can be referenced from Go using C.enum_foo. + f.names["enum_"+name] = c + } + // The enum fields are in global scope, so recurse to visit them. + return C.CXChildVisit_Recurse + case C.CXCursor_EnumConstantDecl: + // We arrive here because of the "Recurse" above. + name := getString(C.tinygo_clang_getCursorSpelling(c)) + f.names[name] = c + } + return C.CXChildVisit_Continue +} + // getCursorPosition returns a usable token.Pos from a libclang cursor. func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos { return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor)) @@ -391,7 +588,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) { // makeDecayingASTType does the same as makeASTType but takes care of decaying // types (arrays in function parameters, etc). It is otherwise identical to // makeASTType. -func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { +func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { // Strip typedefs, if any. underlyingType := typ if underlyingType.kind == C.CXType_Typedef { @@ -417,15 +614,15 @@ func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { pointeeType := C.clang_getElementType(underlyingType) return &ast.StarExpr{ Star: pos, - X: p.makeASTType(pointeeType, pos), + X: f.makeASTType(pointeeType, pos), } } - return p.makeASTType(typ, pos) + return f.makeASTType(typ, pos) } // makeASTType return the ast.Expr for the given libclang type. In other words, // it converts a libclang type to a type in the Go AST. -func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { +func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { var typeName string switch typ.kind { case C.CXType_Char_S, C.CXType_Char_U: @@ -486,7 +683,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { } return &ast.StarExpr{ Star: pos, - X: p.makeASTType(pointeeType, pos), + X: f.makeASTType(pointeeType, pos), } case C.CXType_ConstantArray: return &ast.ArrayType{ @@ -496,7 +693,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { Kind: token.INT, Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10), }, - Elt: p.makeASTType(C.clang_getElementType(typ), pos), + Elt: f.makeASTType(C.clang_getElementType(typ), pos), } case C.CXType_FunctionProto: // Be compatible with gc, which uses the *[0]byte type for function @@ -517,71 +714,21 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { } case C.CXType_Typedef: name := getString(C.clang_getTypedefName(typ)) - if _, ok := p.typedefs[name]; !ok { - p.typedefs[name] = nil // don't recurse - c := C.tinygo_clang_getTypeDeclaration(typ) - underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) - expr := p.makeASTType(underlyingType, pos) - if strings.HasPrefix(name, "_Cgo_") { - expr := expr.(*ast.Ident) - typeSize := C.clang_Type_getSizeOf(underlyingType) - switch expr.Name { - case "C.char": - if typeSize != 1 { - // This happens for some very special purpose architectures - // (DSPs etc.) that are not currently targeted. - // https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/ - p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize)) - } - switch underlyingType.kind { - case C.CXType_Char_S: - expr.Name = "int8" - case C.CXType_Char_U: - expr.Name = "uint8" - } - case "C.schar", "C.short", "C.int", "C.long", "C.longlong": - switch typeSize { - case 1: - expr.Name = "int8" - case 2: - expr.Name = "int16" - case 4: - expr.Name = "int32" - case 8: - expr.Name = "int64" - } - case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong": - switch typeSize { - case 1: - expr.Name = "uint8" - case 2: - expr.Name = "uint16" - case 4: - expr.Name = "uint32" - case 8: - expr.Name = "uint64" - } - } - } - p.typedefs[name] = &typedefInfo{ - typeExpr: expr, - pos: pos, - } - } + c := C.tinygo_clang_getTypeDeclaration(typ) return &ast.Ident{ NamePos: pos, - Name: "C." + name, + Name: f.getASTDeclName(name, c, false), } case C.CXType_Elaborated: underlying := C.clang_Type_getNamedType(typ) switch underlying.kind { case C.CXType_Record: - return p.makeASTType(underlying, pos) + return f.makeASTType(underlying, pos) case C.CXType_Enum: - return p.makeASTType(underlying, pos) + return f.makeASTType(underlying, pos) default: typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind)) - p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling)) + f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling)) typeName = "<unknown>" } case C.CXType_Record: @@ -599,63 +746,46 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { } if name == "" { // Anonymous record, probably inside a typedef. - 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 := cgoRecordPrefix + strconv.Itoa(p.anonStructNum) - p.elaboratedTypes[cgoName] = typeInfo - return &ast.Ident{ - NamePos: pos, - Name: "C." + cgoName, - } + clangLocation := C.tinygo_clang_getCursorLocation(cursor) + var file C.CXFile + var line C.unsigned + var column C.unsigned + C.clang_getFileLocation(clangLocation, &file, &line, &column, nil) + location := token.Position{ + Filename: getString(C.clang_getFileName(file)), + Line: int(line), + Column: int(column), } - return typeInfo.typeExpr - } else { - cgoName := cgoRecordPrefix + name - if _, ok := p.elaboratedTypes[cgoName]; !ok { - p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion) - p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos) - } - return &ast.Ident{ - NamePos: pos, - Name: "C." + cgoName, + if location.Filename == "" || location.Line == 0 { + // Not sure when this would happen, but protect from it anyway. + f.addError(pos, "could not find file/line information") } + name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location) + } else { + name = cgoRecordPrefix + name + } + return &ast.Ident{ + NamePos: pos, + Name: f.getASTDeclName(name, cursor, false), } case C.CXType_Enum: cursor := C.tinygo_clang_getTypeDeclaration(typ) name := getString(C.tinygo_clang_getCursorSpelling(cursor)) - underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor) if name == "" { - // anonymous enum - ref := storedRefs.Put(p) - defer storedRefs.Remove(ref) - C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref)) - return p.makeASTType(underlying, pos) + name = f.getUnnamedDeclName("_Ctype_enum___", cursor) } else { - // named enum - if _, ok := p.enums[name]; !ok { - ref := storedRefs.Put(p) - defer storedRefs.Remove(ref) - C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref)) - p.enums[name] = enumInfo{ - typeExpr: p.makeASTType(underlying, pos), - pos: pos, - } - } - return &ast.Ident{ - NamePos: pos, - Name: "C.enum_" + name, - } + name = "enum_" + name + } + return &ast.Ident{ + NamePos: pos, + Name: f.getASTDeclName(name, cursor, false), } } if typeName == "" { // Report this as an error. typeSpelling := getString(C.clang_getTypeSpelling(typ)) typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind)) - p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling)) + f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling)) typeName = "C.<unknown>" } return &ast.Ident{ @@ -664,9 +794,80 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { } } +// getIntegerType returns an AST node that defines types such as C.int. +func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec { + pos := p.getCursorPosition(cursor) + + // Find a Go type that matches the size and signedness of the given C type. + underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor) + var goName string + typeSize := C.clang_Type_getSizeOf(underlyingType) + switch name { + case "C.char": + if typeSize != 1 { + // This happens for some very special purpose architectures + // (DSPs etc.) that are not currently targeted. + // https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/ + p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize)) + } + switch underlyingType.kind { + case C.CXType_Char_S: + goName = "int8" + case C.CXType_Char_U: + goName = "uint8" + } + case "C.schar", "C.short", "C.int", "C.long", "C.longlong": + switch typeSize { + case 1: + goName = "int8" + case 2: + goName = "int16" + case 4: + goName = "int32" + case 8: + goName = "int64" + } + case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong": + switch typeSize { + case 1: + goName = "uint8" + case 2: + goName = "uint16" + case 4: + goName = "uint32" + case 8: + goName = "uint64" + } + } + + if goName == "" { // should not happen + p.addError(pos, "internal error: did not find Go type for C type "+name) + goName = "int" + } + + // Construct an *ast.TypeSpec for this type. + obj := &ast.Object{ + Kind: ast.Typ, + Name: name, + } + spec := &ast.TypeSpec{ + Name: &ast.Ident{ + NamePos: pos, + Name: name, + Obj: obj, + }, + Type: &ast.Ident{ + NamePos: pos, + Name: goName, + }, + } + obj.Decl = spec + return spec +} + // makeASTRecordType parses a C record (struct or union) and translates it into // a Go struct type. -func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo { +func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo { fieldList := &ast.FieldList{ Opening: pos, Closing: pos, @@ -676,11 +877,11 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab bitfieldNum := 0 ref := storedRefs.Put(struct { fieldList *ast.FieldList - pkg *cgoPackage + file *cgoFile inBitfield *bool bitfieldNum *int bitfieldList *[]bitfieldInfo - }{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList}) + }{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList}) defer storedRefs.Remove(ref) C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref)) renameFieldKeywords(fieldList) @@ -709,13 +910,13 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab } if bitfieldList != nil { // This is valid C... but please don't do this. - p.addError(pos, "bitfield in a union is not supported") + f.addError(pos, "bitfield in a union is not supported") } 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") + f.addError(pos, "zero-length union is not supported") } typeInfo.unionSize = sizeInBytes typeInfo.unionAlign = alignInBytes @@ -723,7 +924,7 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab 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)) + f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling)) return &elaboratedTypeInfo{ typeExpr: &ast.StructType{ Struct: pos, @@ -737,17 +938,17 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elab func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct { fieldList *ast.FieldList - pkg *cgoPackage + file *cgoFile inBitfield *bool bitfieldNum *int bitfieldList *[]bitfieldInfo }) fieldList := passed.fieldList - p := passed.pkg + f := passed.file inBitfield := passed.inBitfield bitfieldNum := passed.bitfieldNum bitfieldList := passed.bitfieldList - pos := p.getCursorPosition(c) + pos := f.getCursorPosition(c) switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind { case C.CXCursor_FieldDecl: // Expected. This is a regular field. @@ -756,7 +957,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD return C.CXChildVisit_Continue default: cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind)) - p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling)) + f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling)) return C.CXChildVisit_Continue } name := getString(C.tinygo_clang_getCursorSpelling(c)) @@ -767,14 +968,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD } typ := C.tinygo_clang_getCursorType(c) field := &ast.Field{ - Type: p.makeASTType(typ, p.getCursorPosition(c)), + Type: f.makeASTType(typ, f.getCursorPosition(c)), } offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name))) alignOf := int64(C.clang_Type_getAlignOf(typ) * 8) bitfieldOffset := offsetof % alignOf if bitfieldOffset != 0 { if C.tinygo_clang_Cursor_isBitField(c) != 1 { - p.addError(pos, "expected a bitfield") + f.addError(pos, "expected a bitfield") return C.CXChildVisit_Continue } if !*inBitfield { @@ -821,23 +1022,6 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD return C.CXChildVisit_Continue } -//export tinygo_clang_enum_visitor -func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { - p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage) - name := getString(C.tinygo_clang_getCursorSpelling(c)) - pos := p.getCursorPosition(c) - value := C.tinygo_clang_getEnumConstantDeclValue(c) - p.constants[name] = constantInfo{ - expr: &ast.BasicLit{ - ValuePos: pos, - Kind: token.INT, - Value: strconv.FormatInt(int64(value), 10), - }, - pos: pos, - } - return C.CXChildVisit_Continue -} - //export tinygo_clang_inclusion_visitor func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) { callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile)) diff --git a/cgo/testdata/basic.out.go b/cgo/testdata/basic.out.go index 125605de8..f2daa78da 100644 --- a/cgo/testdata/basic.out.go +++ b/cgo/testdata/basic.out.go @@ -24,23 +24,16 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte { return C.__GoBytes(ptr, uintptr(length)) } -type C.int16_t = int16 -type C.int32_t = int32 -type C.int64_t = int64 -type C.int8_t = int8 -type C.uint16_t = uint16 -type C.uint32_t = uint32 -type C.uint64_t = uint64 -type C.uint8_t = uint8 -type C.uintptr_t = uintptr -type C.char uint8 -type C.int int32 -type C.long int32 -type C.longlong int64 -type C.schar int8 -type C.short int16 -type C.uchar uint8 -type C.uint uint32 -type C.ulong uint32 -type C.ulonglong uint64 -type C.ushort uint16 +type ( + C.char uint8 + C.schar int8 + C.uchar uint8 + C.short int16 + C.ushort uint16 + C.int int32 + C.uint uint32 + C.long int32 + C.ulong uint32 + C.longlong int64 + C.ulonglong uint64 +) diff --git a/cgo/testdata/const.out.go b/cgo/testdata/const.out.go index e12cded4f..4a5f5fd5b 100644 --- a/cgo/testdata/const.out.go +++ b/cgo/testdata/const.out.go @@ -24,26 +24,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte { return C.__GoBytes(ptr, uintptr(length)) } -const C.bar = C.foo -const C.foo = 3 +type ( + C.char uint8 + C.schar int8 + C.uchar uint8 + C.short int16 + C.ushort uint16 + C.int int32 + C.uint uint32 + C.long int32 + C.ulong uint32 + C.longlong int64 + C.ulonglong uint64 +) -type C.int16_t = int16 -type C.int32_t = int32 -type C.int64_t = int64 -type C.int8_t = int8 -type C.uint16_t = uint16 -type C.uint32_t = uint32 -type C.uint64_t = uint64 -type C.uint8_t = uint8 -type C.uintptr_t = uintptr -type C.char uint8 -type C.int int32 -type C.long int32 -type C.longlong int64 -type C.schar int8 -type C.short int16 -type C.uchar uint8 -type C.uint uint32 -type C.ulong uint32 -type C.ulonglong uint64 -type C.ushort uint16 +const C.foo = 3 +const C.bar = C.foo diff --git a/cgo/testdata/errors.go b/cgo/testdata/errors.go index 8724caf73..7ca5b7960 100644 --- a/cgo/testdata/errors.go +++ b/cgo/testdata/errors.go @@ -27,7 +27,7 @@ import "C" //line errors.go:100 var ( // constant too large - _ C.uint8_t = 2 << 10 + _ C.char = 2 << 10 // z member does not exist _ C.point_t = C.point_t{z: 3} diff --git a/cgo/testdata/errors.out.go b/cgo/testdata/errors.out.go index 8200a459f..b15a26b66 100644 --- a/cgo/testdata/errors.out.go +++ b/cgo/testdata/errors.out.go @@ -6,7 +6,7 @@ // testdata/errors.go:19:26: unexpected token ), expected end of expression // Type checking errors after CGo processing: -// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows) +// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows) // testdata/errors.go:105: unknown field z in struct literal // testdata/errors.go:108: undeclared name: C.SOME_CONST_1 // testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows) @@ -38,29 +38,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte { return C.__GoBytes(ptr, uintptr(length)) } -const C.SOME_CONST_3 = 1234 - -type C.int16_t = int16 -type C.int32_t = int32 -type C.int64_t = int64 -type C.int8_t = int8 -type C.uint16_t = uint16 -type C.uint32_t = uint32 -type C.uint64_t = uint64 -type C.uint8_t = uint8 -type C.uintptr_t = uintptr -type C.char uint8 -type C.int int32 -type C.long int32 -type C.longlong int64 -type C.schar int8 -type C.short int16 -type C.uchar uint8 -type C.uint uint32 -type C.ulong uint32 -type C.ulonglong uint64 -type C.ushort uint16 -type C.point_t = struct { +type ( + C.char uint8 + C.schar int8 + C.uchar uint8 + C.short int16 + C.ushort uint16 + C.int int32 + C.uint uint32 + C.long int32 + C.ulong uint32 + C.longlong int64 + C.ulonglong uint64 +) +type C._Ctype_struct___0 struct { x C.int y C.int } +type C.point_t = C._Ctype_struct___0 + +const C.SOME_CONST_3 = 1234 diff --git a/cgo/testdata/flags.out.go b/cgo/testdata/flags.out.go index 920778655..72520ab6d 100644 --- a/cgo/testdata/flags.out.go +++ b/cgo/testdata/flags.out.go @@ -29,26 +29,19 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte { return C.__GoBytes(ptr, uintptr(length)) } +type ( + C.char uint8 + C.schar int8 + C.uchar uint8 + C.short int16 + C.ushort uint16 + C.int int32 + C.uint uint32 + C.long int32 + C.ulong uint32 + C.longlong int64 + C.ulonglong uint64 +) + const C.BAR = 3 const C.FOO_H = 1 - -type C.int16_t = int16 -type C.int32_t = int32 -type C.int64_t = int64 -type C.int8_t = int8 -type C.uint16_t = uint16 -type C.uint32_t = uint32 -type C.uint64_t = uint64 -type C.uint8_t = uint8 -type C.uintptr_t = uintptr -type C.char uint8 -type C.int int32 -type C.long int32 -type C.longlong int64 -type C.schar int8 -type C.short int16 -type C.uchar uint8 -type C.uint uint32 -type C.ulong uint32 -type C.ulonglong uint64 -type C.ushort uint16 diff --git a/cgo/testdata/symbols.out.go b/cgo/testdata/symbols.out.go index 53ec4f0b0..6791999ad 100644 --- a/cgo/testdata/symbols.out.go +++ b/cgo/testdata/symbols.out.go @@ -24,41 +24,36 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte { return C.__GoBytes(ptr, uintptr(length)) } +type ( + C.char uint8 + C.schar int8 + C.uchar uint8 + C.short int16 + C.ushort uint16 + C.int int32 + C.uint uint32 + C.long int32 + C.ulong uint32 + C.longlong int64 + C.ulonglong uint64 +) + //export foo func C.foo(a C.int, b C.int) C.int +var C.foo$funcaddr unsafe.Pointer + //export variadic0 //go:variadic func C.variadic0() +var C.variadic0$funcaddr unsafe.Pointer + //export variadic2 //go:variadic func C.variadic2(x C.int, y C.int) -var C.foo$funcaddr unsafe.Pointer -var C.variadic0$funcaddr unsafe.Pointer var C.variadic2$funcaddr unsafe.Pointer //go:extern someValue var C.someValue C.int - -type C.int16_t = int16 -type C.int32_t = int32 -type C.int64_t = int64 -type C.int8_t = int8 -type C.uint16_t = uint16 -type C.uint32_t = uint32 -type C.uint64_t = uint64 -type C.uint8_t = uint8 -type C.uintptr_t = uintptr -type C.char uint8 -type C.int int32 -type C.long int32 -type C.longlong int64 -type C.schar int8 -type C.short int16 -type C.uchar uint8 -type C.uint uint32 -type C.ulong uint32 -type C.ulonglong uint64 -type C.ushort uint16 diff --git a/cgo/testdata/types.out.go b/cgo/testdata/types.out.go index 639d1f4f3..bd7b3fc42 100644 --- a/cgo/testdata/types.out.go +++ b/cgo/testdata/types.out.go @@ -24,132 +24,126 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte { return C.__GoBytes(ptr, uintptr(length)) } -const C.option2A = 20 -const C.optionA = 0 -const C.optionB = 1 -const C.optionC = -5 -const C.optionD = -4 -const C.optionE = 10 -const C.optionF = 11 -const C.optionG = 12 -const C.unused1 = 5 - -type C.int16_t = int16 -type C.int32_t = int32 -type C.int64_t = int64 -type C.int8_t = int8 -type C.uint16_t = uint16 -type C.uint32_t = uint32 -type C.uint64_t = uint64 -type C.uint8_t = uint8 -type C.uintptr_t = uintptr -type C.char uint8 -type C.int int32 -type C.long int32 -type C.longlong int64 -type C.schar int8 -type C.short int16 -type C.uchar uint8 -type C.uint uint32 -type C.ulong uint32 -type C.ulonglong uint64 -type C.ushort uint16 -type C.bitfield_t = C.struct_4 -type C.myIntArray = [10]C.int +type ( + C.char uint8 + C.schar int8 + C.uchar uint8 + C.short int16 + C.ushort uint16 + C.int int32 + C.uint uint32 + C.long int32 + C.ulong uint32 + C.longlong int64 + C.ulonglong uint64 +) type C.myint = C.int -type C.option2_t = C.uint -type C.option_t = C.enum_option -type C.point2d_t = struct { +type C._Ctype_struct___0 struct { x C.int y C.int } -type C.point3d_t = C.struct_point3d -type C.struct_nested_t = struct { - begin C.point2d_t - end C.point2d_t - tag C.int - - coord C.union_2 -} -type C.types_t = struct { - f float32 - 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.union_nested_t = C.union_3 -type C.unionarray_t = struct{ arr [10]C.uchar } - -func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } -func (s *C.struct_4) set_bitfield_a(value C.uchar) { - s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 -} -func (s *C.struct_4) bitfield_b() C.uchar { - return s.__bitfield_1 >> 5 & 0x1 -} -func (s *C.struct_4) set_bitfield_b(value C.uchar) { - s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 -} -func (s *C.struct_4) bitfield_c() C.uchar { - return s.__bitfield_1 >> 6 -} -func (s *C.struct_4) set_bitfield_c(value C.uchar, - -) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 } - -type C.struct_4 struct { - start C.uchar - __bitfield_1 C.uchar - - d C.uchar - e C.uchar -} +type C.point2d_t = C._Ctype_struct___0 type C.struct_point3d struct { x C.int y C.int z C.int } +type C.point3d_t = C.struct_point3d type C.struct_type1 struct { _type C.int __type C.int ___type C.int } type C.struct_type2 struct{ _type C.int } +type C._Ctype_union___1 struct{ i C.int } +type C.union1_t = C._Ctype_union___1 +type C._Ctype_union___2 struct{ $union uint64 } -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)) } +func (union *C._Ctype_union___2) unionfield_i() *C.int { + return (*C.int)(unsafe.Pointer(&union.$union)) +} +func (union *C._Ctype_union___2) unionfield_d() *float64 { + return (*float64)(unsafe.Pointer(&union.$union)) +} +func (union *C._Ctype_union___2) unionfield_s() *C.short { + return (*C.short)(unsafe.Pointer(&union.$union)) +} -type C.union_1 struct{ $union uint64 } +type C.union3_t = C._Ctype_union___2 +type C.union_union2d struct{ $union [2]uint64 } -func (union *C.union_2) unionfield_area() *C.point2d_t { +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.union2d_t = C.union_union2d +type C._Ctype_union___3 struct{ arr [10]C.uchar } +type C.unionarray_t = C._Ctype_union___3 +type C._Ctype_union___5 struct{ $union [3]uint32 } + +func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t { return (*C.point2d_t)(unsafe.Pointer(&union.$union)) } -func (union *C.union_2) unionfield_solid() *C.point3d_t { +func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t { return (*C.point3d_t)(unsafe.Pointer(&union.$union)) } -type C.union_2 struct{ $union [3]uint32 } +type C._Ctype_struct___4 struct { + begin C.point2d_t + end C.point2d_t + tag C.int + + coord C._Ctype_union___5 +} +type C.struct_nested_t = C._Ctype_struct___4 +type C._Ctype_union___6 struct{ $union [2]uint64 } -func (union *C.union_3) unionfield_point() *C.point3d_t { +func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t { return (*C.point3d_t)(unsafe.Pointer(&union.$union)) } -func (union *C.union_3) unionfield_array() *C.unionarray_t { +func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t { return (*C.unionarray_t)(unsafe.Pointer(&union.$union)) } -func (union *C.union_3) unionfield_thing() *C.union3_t { +func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t { return (*C.union3_t)(unsafe.Pointer(&union.$union)) } -type C.union_3 struct{ $union [2]uint64 } +type C.union_nested_t = C._Ctype_union___6 +type C.enum_option = C.int +type C.option_t = C.enum_option +type C._Ctype_enum___7 = C.uint +type C.option2_t = C._Ctype_enum___7 +type C._Ctype_struct___8 struct { + f float32 + d float64 + ptr *C.int +} +type C.types_t = C._Ctype_struct___8 +type C.myIntArray = [10]C.int +type C._Ctype_struct___9 struct { + start C.uchar + __bitfield_1 C.uchar + + d C.uchar + e C.uchar +} -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)) +func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } +func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) { + s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 } +func (s *C._Ctype_struct___9) bitfield_b() C.uchar { + return s.__bitfield_1 >> 5 & 0x1 +} +func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) { + s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 +} +func (s *C._Ctype_struct___9) bitfield_c() C.uchar { + return s.__bitfield_1 >> 6 +} +func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar, -type C.union_union2d struct{ $union [2]uint64 } -type C.enum_option C.int -type C.enum_unused C.uint +) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 } + +type C.bitfield_t = C._Ctype_struct___9 |