diff --git a/cgo/libclang.go b/cgo/libclang.go index 3a496aa2..f27d7935 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -196,7 +196,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient } fn.args = append(fn.args, paramInfo{ name: argName, - typeExpr: p.makeASTType(argType, pos), + typeExpr: p.makeDecayingASTType(argType, pos), }) } resultType := C.tinygo_clang_getCursorResultType(c) @@ -391,6 +391,41 @@ 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 { + // Strip typedefs, if any. + underlyingType := typ + if underlyingType.kind == C.CXType_Typedef { + c := C.tinygo_clang_getTypeDeclaration(typ) + underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c) + // TODO: support a chain of typedefs. At the moment, it seems to get + // stuck in an endless loop when trying to get to the most underlying + // type. + } + // Check for decaying type. An example would be an array type in a + // parameter. This declaration: + // void foo(char buf[6]); + // is the same as this one: + // void foo(char *buf); + // But this one: + // void bar(char buf[6][4]); + // equals this: + // void bar(char *buf[4]); + // so not all array dimensions should be stripped, just the first one. + // TODO: there are more kinds of decaying types. + if underlyingType.kind == C.CXType_ConstantArray { + // Apply type decaying. + pointeeType := C.clang_getElementType(underlyingType) + return &ast.StarExpr{ + Star: pos, + X: p.makeASTType(pointeeType, pos), + } + } + return p.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 { diff --git a/testdata/cgo/main.c b/testdata/cgo/main.c index 7954b91f..d4083324 100644 --- a/testdata/cgo/main.c +++ b/testdata/cgo/main.c @@ -61,3 +61,7 @@ void unionSetData(short f0, short f1, short f2) { globalUnion.data[1] = 8; globalUnion.data[2] = 1; } + +void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]) { + // Do nothing. +} diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index 113176df..b1880c7b 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -128,6 +128,12 @@ func main() { // Check whether CFLAGS are correctly passed on to compiled C files. println("CFLAGS value:", C.cflagsConstant) + // Check array-to-pointer decaying. This signature: + // void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]); + // decays to: + // void arraydecay(int *buf1, int *buf2[8], int *buf3[7][2]); + C.arraydecay((*C.int)(nil), (*[8]C.int)(nil), (*[7][2]C.int)(nil)) + // libc: test whether C functions work at all. buf1 := []byte("foobar\x00") buf2 := make([]byte, len(buf1)) diff --git a/testdata/cgo/main.h b/testdata/cgo/main.h index ddd07efa..09e1b4f0 100644 --- a/testdata/cgo/main.h +++ b/testdata/cgo/main.h @@ -144,3 +144,7 @@ extern int cflagsConstant; // test duplicate definitions int add(int a, int b); extern int global; + +// Test array decaying into a pointer. +typedef int arraydecay_buf3[4][7][2]; +void arraydecay(int buf1[5], int buf2[3][8], arraydecay_buf3 buf3);