Skip to content

Commit 21a4c14

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent b716cf1 commit 21a4c14

File tree

5 files changed

+89
-13
lines changed

5 files changed

+89
-13
lines changed

loader/cgo.go

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ import (
1717
type fileInfo struct {
1818
*ast.File
1919
*Package
20-
filename string
21-
functions map[string]*functionInfo
22-
globals map[string]*globalInfo
23-
typedefs map[string]*typedefInfo
24-
importCPos token.Pos
20+
filename string
21+
functions map[string]*functionInfo
22+
globals map[string]*globalInfo
23+
typedefs map[string]*typedefInfo
24+
elaboratedTypes map[string]ast.Expr
25+
importCPos token.Pos
2526
}
2627

2728
// functionInfo stores some information about a Cgo function found by libclang
@@ -81,12 +82,13 @@ typedef unsigned long long _Cgo_ulonglong;
8182
// comment with libclang, and modifies the AST to use this information.
8283
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
8384
info := &fileInfo{
84-
File: f,
85-
Package: p,
86-
filename: filename,
87-
functions: map[string]*functionInfo{},
88-
globals: map[string]*globalInfo{},
89-
typedefs: map[string]*typedefInfo{},
85+
File: f,
86+
Package: p,
87+
filename: filename,
88+
functions: map[string]*functionInfo{},
89+
globals: map[string]*globalInfo{},
90+
typedefs: map[string]*typedefInfo{},
91+
elaboratedTypes: map[string]ast.Expr{},
9092
}
9193

9294
// Find `import "C"` statements in the file.
@@ -142,9 +144,12 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []er
142144
// Forward C types to Go types (like C.uint32_t -> uint32).
143145
info.addTypeAliases()
144146

145-
// Add type declarations for C types, declared using typeef in C.
147+
// Add type declarations for C types, declared using typedef in C.
146148
info.addTypedefs()
147149

150+
// Add elaborated types for C structs and unions.
151+
info.addElaboratedTypes()
152+
148153
// Patch the AST to use the declared types and functions.
149154
f = astutil.Apply(f, info.walker, nil).(*ast.File)
150155

@@ -376,6 +381,42 @@ func (info *fileInfo) addTypedefs() {
376381
info.Decls = append(info.Decls, gen)
377382
}
378383

384+
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
385+
// foo" or "union foo" types, often used in a typedef.
386+
//
387+
// See also:
388+
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
389+
func (info *fileInfo) addElaboratedTypes() {
390+
gen := &ast.GenDecl{
391+
TokPos: info.importCPos,
392+
Tok: token.TYPE,
393+
}
394+
names := make([]string, 0, len(info.elaboratedTypes))
395+
for name := range info.elaboratedTypes {
396+
names = append(names, name)
397+
}
398+
sort.Strings(names)
399+
for _, name := range names {
400+
typ := info.elaboratedTypes[name]
401+
typeName := "C.struct_" + name
402+
obj := &ast.Object{
403+
Kind: ast.Typ,
404+
Name: typeName,
405+
}
406+
typeSpec := &ast.TypeSpec{
407+
Name: &ast.Ident{
408+
NamePos: info.importCPos,
409+
Name: typeName,
410+
Obj: obj,
411+
},
412+
Type: typ,
413+
}
414+
obj.Decl = typeSpec
415+
gen.Specs = append(gen.Specs, typeSpec)
416+
}
417+
info.Decls = append(info.Decls, gen)
418+
}
419+
379420
// walker replaces all "C".<something> expressions to literal "C.<something>"
380421
// expressions. Such expressions are impossible to write in Go (a dot cannot be
381422
// used in the middle of a name) so in practice all C identifiers live in a

loader/libclang.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,25 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
332332
}
333333
case C.CXType_Elaborated:
334334
underlying := C.clang_Type_getNamedType(typ)
335-
return info.makeASTType(underlying)
335+
switch underlying.kind {
336+
case C.CXType_Record:
337+
cursor := C.tinygo_clang_getTypeDeclaration(typ)
338+
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
339+
// It is possible that this is a recursive definition, for example
340+
// in linked lists (structs contain a pointer to the next element
341+
// of the same type). If the name exists in info.elaboratedTypes,
342+
// it is being processed, although it may not be fully defined yet.
343+
if _, ok := info.elaboratedTypes[name]; !ok {
344+
info.elaboratedTypes[name] = nil // predeclare (to avoid endless recursion)
345+
info.elaboratedTypes[name] = info.makeASTType(underlying)
346+
}
347+
return &ast.Ident{
348+
NamePos: info.importCPos,
349+
Name: "C.struct_" + name,
350+
}
351+
default:
352+
panic("unknown elaborated type")
353+
}
336354
case C.CXType_Record:
337355
cursor := C.tinygo_clang_getTypeDeclaration(typ)
338356
fieldList := &ast.FieldList{

testdata/cgo/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ func main() {
5353
C.unionSetData(5, 8, 1)
5454
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
5555
println("union field:", printUnion(C.globalUnion).f)
56+
57+
// recursive types, test using a linked list
58+
lastElement := &C.list_t{n: 7, next: nil}
59+
list := &C.list_t{n: 3, next: &C.struct_list_t{n: 6, next: (*C.struct_list_t)(lastElement)}}
60+
for list != nil {
61+
println("n in chain:", list.n)
62+
list = (*C.list_t)(list.next)
63+
}
5664
}
5765

5866
func printUnion(union C.joined_t) C.joined_t {

testdata/cgo/main.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ typedef struct collection {
1212
unsigned char c;
1313
} collection_t;
1414

15+
// linked list
16+
typedef struct list_t {
17+
int n;
18+
struct list_t *next;
19+
} list_t;
20+
1521
typedef union joined {
1622
myint s;
1723
float f;

testdata/cgo/out.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ union local data: 5 8 1
2424
union s method: -33 false
2525
union f: +6.280000e+000
2626
union field: +6.280000e+000
27+
n in chain: 3
28+
n in chain: 6
29+
n in chain: 7

0 commit comments

Comments
 (0)