From 3ecefd6a0a4fecac1553db802dea50784b6ea3f7 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 22 Sep 2023 15:51:44 +0200 Subject: [PATCH] cgo: refactor Go types a little bit Code like this is not allowed by the upstream Go CGo implementation, but was allowed by TinyGo: var _ int8 = C.int8_t(5) The reason it shouldn't be allowed is a little bit complicated. While it is true that C.int8_t is always the same underlying data type as Go int8 (signed 8-bit integer), the C type is actually a typedef of one of the base C types (usually unsigned char or signed char) which in turn do _not_ map cleanly to Go types: the 'char' type is ambiguous (it may be either signed or unsigned depending on the ABI) and types like 'int' vary in size by ABI as well. To make code more portable, I think it's better to match the upstream implementation. --- cgo/cgo.go | 28 +++++++++------------------- cgo/testdata/errors.go | 8 ++++++++ cgo/testdata/errors.out.go | 3 +++ testdata/cgo/main.go | 4 ---- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/cgo/cgo.go b/cgo/cgo.go index 67979230cc..b00d58e5c4 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -75,19 +75,10 @@ type bitfieldInfo struct { } // cgoAliases list type aliases between Go and C, for types that are equivalent -// in both languages. See addTypeAliases. +// in both languages. var cgoAliases = map[string]string{ - "C.int8_t": "int8", - "C.int16_t": "int16", - "C.int32_t": "int32", - "C.int64_t": "int64", - "C.uint8_t": "uint8", - "C.uint16_t": "uint16", - "C.uint32_t": "uint32", - "C.uint64_t": "uint64", - "C.uintptr_t": "uintptr", - "C.float": "float32", - "C.double": "float64", + "float": "float32", + "double": "float64", } // builtinAliases are handled specially because they only exist on the Go side @@ -311,10 +302,11 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl // Process CGo imports for each file. for i, f := range files { cf := p.newCGoFile(f, i) - // Float and double are aliased, meaning that C.float is the same thing - // as float32 in Go. - cf.names["float"] = clangCursor{} - cf.names["double"] = clangCursor{} + // These types are aliased with the corresponding types in C. For + // example, float in C is always float32 in Go. + for name := range cgoAliases { + cf.names[name] = clangCursor{} + } // Now read all the names (identifies) that C defines in the header // snippet. cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) { @@ -1129,9 +1121,7 @@ func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string { // getASTDeclName will declare the given C AST node (if not already defined) and // will return its name, in the form of C.foo. func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string { - // Some types are defined in stdint.h and map directly to a particular Go - // type. - if alias := cgoAliases["C."+name]; alias != "" { + if alias := cgoAliases[name]; alias != "" { return alias } node := f.getASTDeclNode(name, found, iscall) diff --git a/cgo/testdata/errors.go b/cgo/testdata/errors.go index 7ca5b79600..0ce553c1de 100644 --- a/cgo/testdata/errors.go +++ b/cgo/testdata/errors.go @@ -22,8 +22,12 @@ import "C" // #warning another warning import "C" +// #include +import "C" + // Make sure that errors for the following lines won't change with future // additions to the CGo preamble. +// //line errors.go:100 var ( // constant too large @@ -38,4 +42,8 @@ var ( _ byte = C.SOME_CONST_3 _ = C.SOME_CONST_4 + + // This must result in a type error. Previously, TinyGo would allow this + // code (which is not allowed by upstream Go). + _ int8 = C.int8_t(5) ) diff --git a/cgo/testdata/errors.out.go b/cgo/testdata/errors.out.go index b1646a2e0d..5c86d9748f 100644 --- a/cgo/testdata/errors.out.go +++ b/cgo/testdata/errors.out.go @@ -11,6 +11,7 @@ // testdata/errors.go:108: undefined: C.SOME_CONST_1 // testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows) // testdata/errors.go:112: undefined: C.SOME_CONST_4 +// testdata/errors.go:116: cannot use C.int8_t(5) (constant 5 of type C.schar) as int8 value in variable declaration package main @@ -58,3 +59,5 @@ type C.struct_point_t struct { type C.point_t = C.struct_point_t const C.SOME_CONST_3 = 1234 + +type C.int8_t = C.schar diff --git a/testdata/cgo/main.go b/testdata/cgo/main.go index 4555c922f9..2e8dc257d4 100644 --- a/testdata/cgo/main.go +++ b/testdata/cgo/main.go @@ -52,10 +52,6 @@ func main() { println("static headerfunc:", C.headerfunc_static(5)) headerfunc_2() - // equivalent types - var goInt8 int8 = 5 - var _ C.int8_t = goInt8 - // more globals println("bool:", C.globalBool, C.globalBool2 == true) println("float:", C.globalFloat)