diff options
author | Ayke van Laethem <[email protected]> | 2019-04-20 02:39:31 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2019-04-20 10:18:38 +0200 |
commit | 21a4c14e86e5f30ff3c8ed509f6b2d0f408df08b (patch) | |
tree | c94c5898ee0295c5b9c55776a5c20d4b10df87d5 /loader | |
parent | b716cf1afdb6365cbbeb4af974c27c916db36a9a (diff) | |
download | tinygo-21a4c14e86e5f30ff3c8ed509f6b2d0f408df08b.tar.gz tinygo-21a4c14e86e5f30ff3c8ed509f6b2d0f408df08b.zip |
cgo: implement C.struct_ types
These types (called elaborated types in C) are used as part of linked
lists, among others.
This is part an extra feature (to be compatible with CGo C.struct_
types) and part a bugfix: linked lists would result in endless recursion
leading to a stack overflow.
Diffstat (limited to 'loader')
-rw-r--r-- | loader/cgo.go | 65 | ||||
-rw-r--r-- | loader/libclang.go | 20 |
2 files changed, 72 insertions, 13 deletions
diff --git a/loader/cgo.go b/loader/cgo.go index abb5847c6..dc217f962 100644 --- a/loader/cgo.go +++ b/loader/cgo.go @@ -17,11 +17,12 @@ import ( type fileInfo struct { *ast.File *Package - filename string - functions map[string]*functionInfo - globals map[string]*globalInfo - typedefs map[string]*typedefInfo - importCPos token.Pos + filename string + functions map[string]*functionInfo + globals map[string]*globalInfo + typedefs map[string]*typedefInfo + elaboratedTypes map[string]ast.Expr + importCPos token.Pos } // functionInfo stores some information about a Cgo function found by libclang @@ -81,12 +82,13 @@ typedef unsigned long long _Cgo_ulonglong; // comment with libclang, and modifies the AST to use this information. func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error { info := &fileInfo{ - File: f, - Package: p, - filename: filename, - functions: map[string]*functionInfo{}, - globals: map[string]*globalInfo{}, - typedefs: map[string]*typedefInfo{}, + File: f, + Package: p, + filename: filename, + functions: map[string]*functionInfo{}, + globals: map[string]*globalInfo{}, + typedefs: map[string]*typedefInfo{}, + elaboratedTypes: map[string]ast.Expr{}, } // Find `import "C"` statements in the file. @@ -142,9 +144,12 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er // Forward C types to Go types (like C.uint32_t -> uint32). info.addTypeAliases() - // Add type declarations for C types, declared using typeef in C. + // Add type declarations for C types, declared using typedef in C. info.addTypedefs() + // Add elaborated types for C structs and unions. + info.addElaboratedTypes() + // Patch the AST to use the declared types and functions. f = astutil.Apply(f, info.walker, nil).(*ast.File) @@ -376,6 +381,42 @@ func (info *fileInfo) addTypedefs() { info.Decls = append(info.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 (info *fileInfo) addElaboratedTypes() { + gen := &ast.GenDecl{ + TokPos: info.importCPos, + Tok: token.TYPE, + } + names := make([]string, 0, len(info.elaboratedTypes)) + for name := range info.elaboratedTypes { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + typ := info.elaboratedTypes[name] + typeName := "C.struct_" + name + obj := &ast.Object{ + Kind: ast.Typ, + Name: typeName, + } + typeSpec := &ast.TypeSpec{ + Name: &ast.Ident{ + NamePos: info.importCPos, + Name: typeName, + Obj: obj, + }, + Type: typ, + } + obj.Decl = typeSpec + gen.Specs = append(gen.Specs, typeSpec) + } + info.Decls = append(info.Decls, gen) +} + // 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 diff --git a/loader/libclang.go b/loader/libclang.go index 4adf141a4..4c79fcaf9 100644 --- a/loader/libclang.go +++ b/loader/libclang.go @@ -332,7 +332,25 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr { } case C.CXType_Elaborated: underlying := C.clang_Type_getNamedType(typ) - return info.makeASTType(underlying) + switch underlying.kind { + case C.CXType_Record: + cursor := C.tinygo_clang_getTypeDeclaration(typ) + name := getString(C.tinygo_clang_getCursorSpelling(cursor)) + // It is possible that this is a recursive definition, for example + // in linked lists (structs contain a pointer to the next element + // of the same type). If the name exists in info.elaboratedTypes, + // it is being processed, although it may not be fully defined yet. + if _, ok := info.elaboratedTypes[name]; !ok { + info.elaboratedTypes[name] = nil // predeclare (to avoid endless recursion) + info.elaboratedTypes[name] = info.makeASTType(underlying) + } + return &ast.Ident{ + NamePos: info.importCPos, + Name: "C.struct_" + name, + } + default: + panic("unknown elaborated type") + } case C.CXType_Record: cursor := C.tinygo_clang_getTypeDeclaration(typ) fieldList := &ast.FieldList{ |