From aaa860f1543add7d7b1e84db03773dad1dce2310 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 30 Oct 2022 21:12:57 +0100 Subject: [PATCH] cgo: support anonymous enums included in multiple Go files Anonymous enums (often used in typedefs) triggered a problem that was already solved for structs but wasn't yet solved for enums. So this patch generalizes the code to work for both structs and enums, and adds testing for both. --- cgo/libclang.go | 39 ++++++++++++++++++++++++--------------- testdata/cgo/extra.go | 5 +++++ testdata/cgo/main.go | 5 +++++ testdata/cgo/test.h | 11 +++++++++++ 4 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 testdata/cgo/test.h diff --git a/cgo/libclang.go b/cgo/libclang.go index 3c0d196d..27ac1b57 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -533,6 +533,26 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient return C.CXChildVisit_Continue } +// Get the precise location in the source code. Used for uniquely identifying +// source locations. +func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} { + clangLocation := C.tinygo_clang_getCursorLocation(cursor) + var file C.CXFile + var line C.unsigned + var column C.unsigned + C.clang_getFileLocation(clangLocation, &file, &line, &column, nil) + location := token.Position{ + Filename: getString(C.clang_getFileName(file)), + Line: int(line), + Column: int(column), + } + if location.Filename == "" || location.Line == 0 { + // Not sure when this would happen, but protect from it anyway. + f.addError(pos, "could not find file/line information") + } + return location +} + // getCursorPosition returns a usable token.Pos from a libclang cursor. func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos { return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor)) @@ -788,20 +808,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { } if name == "" { // Anonymous record, probably inside a typedef. - clangLocation := C.tinygo_clang_getCursorLocation(cursor) - var file C.CXFile - var line C.unsigned - var column C.unsigned - C.clang_getFileLocation(clangLocation, &file, &line, &column, nil) - location := token.Position{ - Filename: getString(C.clang_getFileName(file)), - Line: int(line), - Column: int(column), - } - if location.Filename == "" || location.Line == 0 { - // Not sure when this would happen, but protect from it anyway. - f.addError(pos, "could not find file/line information") - } + location := f.getUniqueLocationID(pos, cursor) name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location) } else { name = cgoRecordPrefix + name @@ -814,7 +821,9 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { cursor := C.tinygo_clang_getTypeDeclaration(typ) name := getString(C.tinygo_clang_getCursorSpelling(cursor)) if name == "" { - name = f.getUnnamedDeclName("_Ctype_enum___", cursor) + // Anonymous enum, probably inside a typedef. + location := f.getUniqueLocationID(pos, cursor) + name = f.getUnnamedDeclName("_Ctype_enum___", location) } else { name = "enum_" + name } diff --git a/testdata/cgo/extra.go b/testdata/cgo/extra.go index f8ce946d..401733ba 100644 --- a/testdata/cgo/extra.go +++ b/testdata/cgo/extra.go @@ -2,6 +2,7 @@ package main // Make sure CGo supports multiple files. +// #include "test.h" // int fortytwo(void); // static float headerfunc_static(float a) { return a - 1; } // static void headerfunc_void(int a, int *ptr) { *ptr = a; } @@ -17,4 +18,8 @@ func headerfunc_2() { var n C.int C.headerfunc_void(3, &n) println("static headerfunc void:", n) + + // anonymous structs and enums in multiple Go files + var _ C.teststruct + var _ C.testenum } diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index f4ee532a..4555c922 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -4,6 +4,7 @@ package main #include int fortytwo(void); #include "main.h" +#include "test.h" int mul(int, int); #include #cgo CFLAGS: -DSOME_CONSTANT=17 @@ -128,6 +129,10 @@ func main() { println("option 2A:", C.option2A) println("option 3A:", C.option3A) + // anonymous structs and enums in multiple Go files + var _ C.teststruct + var _ C.testenum + // Check that enums are considered the same width in C and CGo. println("enum width matches:", unsafe.Sizeof(C.option2_t(0)) == uintptr(C.smallEnumWidth)) diff --git a/testdata/cgo/test.h b/testdata/cgo/test.h new file mode 100644 index 00000000..8b5b32ea --- /dev/null +++ b/testdata/cgo/test.h @@ -0,0 +1,11 @@ +#pragma once + +typedef struct { + int a; + int b; +} teststruct; + +typedef enum { + v0, + v1 +} testenum;