aboutsummaryrefslogtreecommitdiffhomepage
path: root/cgo
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2022-02-20 19:54:52 +0100
committerRon Evans <[email protected]>2022-05-06 17:22:22 +0200
commit5afb63df60427bd5ddf7adb8e2d43258c2544ce6 (patch)
treec499ed27941c908fd1ac55c706eb2561e00964fc /cgo
parent1d2c39c3b9e46866161fe18f099d8ac5ab2a6425 (diff)
downloadtinygo-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.go896
-rw-r--r--cgo/libclang.go582
-rw-r--r--cgo/testdata/basic.out.go33
-rw-r--r--cgo/testdata/const.out.go37
-rw-r--r--cgo/testdata/errors.go2
-rw-r--r--cgo/testdata/errors.out.go42
-rw-r--r--cgo/testdata/flags.out.go35
-rw-r--r--cgo/testdata/symbols.out.go41
-rw-r--r--cgo/testdata/types.out.go184
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