aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-09-24 03:05:10 +0200
committerAyke van Laethem <[email protected]>2021-09-27 14:55:02 +0200
commit9d2c93d169a747a77fbac60fc6d4fa3d971d0694 (patch)
tree06e096f09d9ec1aa23db7f06195c56368f98e0c0
parentadb79f2c259bf70c74a2a2be262af7c152825f58 (diff)
downloadtinygo-9d2c93d169a747a77fbac60fc6d4fa3d971d0694.tar.gz
tinygo-9d2c93d169a747a77fbac60fc6d4fa3d971d0694.zip
cgo: implement rudimentary C array decaying
This is just a first step. It's not complete, but it gets some real world C code to parse. This signature, from the ESP-IDF: esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]); Was previously converted to something like this (pseudocode): C.esp_err_t esp_wifi_get_mac(ifx C.wifi_interface_t, mac [6]uint8) But this is not correct. C array parameters will decay. The array is passed by reference instead of by value. Instead, this would be the correct signature: C.esp_err_t esp_wifi_get_mac(ifx C.wifi_interface_t, mac *uint8) So that it can be called like this (using CGo): var mac [6]byte errCode := C.esp_wifi_get_mac(C.ESP_IF_WIFI_AP, &mac[0]) This stores the result in the 6-element array mac.
-rw-r--r--cgo/libclang.go37
-rw-r--r--testdata/cgo/main.c4
-rw-r--r--testdata/cgo/main.go6
-rw-r--r--testdata/cgo/main.h4
4 files changed, 50 insertions, 1 deletions
diff --git a/cgo/libclang.go b/cgo/libclang.go
index 8e9af8a08..e5370a89c 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 7954b91f6..d40833246 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 d3491c91c..2e286baaa 100644
--- a/testdata/cgo/main.go
+++ b/testdata/cgo/main.go
@@ -125,6 +125,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 ddd07efa9..09e1b4f09 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);