aboutsummaryrefslogtreecommitdiffhomepage
path: root/loader
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-04-20 02:39:31 +0200
committerRon Evans <[email protected]>2019-04-20 10:18:38 +0200
commit21a4c14e86e5f30ff3c8ed509f6b2d0f408df08b (patch)
treec94c5898ee0295c5b9c55776a5c20d4b10df87d5 /loader
parentb716cf1afdb6365cbbeb4af974c27c916db36a9a (diff)
downloadtinygo-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.go65
-rw-r--r--loader/libclang.go20
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{