From 67a816932f57ec9563f25d13941a15866a81fe61 Mon Sep 17 00:00:00 2001 From: Mauri de Souza Meneguzzo Date: Wed, 27 Nov 2024 10:49:17 -0300 Subject: [PATCH] cmd/cgo: explicitly use void for functions with no parameters Currently, exported Go functions with no parameters generate C functions with an empty parameter list. In C, a function with an empty parameter list can accept any number of arguments, whereas a function with a single void parameter explicitly declares that it takes no arguments. To align the generated C functions with their Go prototypes, update the code generation to explicitly include a void parameter for functions with no parameters. Change-Id: I6d813815228efda95a7a6a9bbf9c9a787ff4f420 --- .../cgo/internal/testcshared/cshared_test.go | 41 +++++++++++++++++++ .../testdata/issue68411/issue68411.go | 15 +++++++ src/cmd/cgo/out.go | 19 +++++---- 3 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go index 7e9a274d05628e..b098895c299fbc 100644 --- a/src/cmd/cgo/internal/testcshared/cshared_test.go +++ b/src/cmd/cgo/internal/testcshared/cshared_test.go @@ -880,3 +880,44 @@ func TestIssue36233(t *testing.T) { t.Error("missing functions") } } + +func TestIssue68411(t *testing.T) { + globalSkip(t) + testenv.MustHaveCGO(t) + + t.Parallel() + + // Test that the export header uses a void function parameter for + // exported Go functions with no parameters. + + tmpdir := t.TempDir() + + const exportHeader = "issue68411.h" + + run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue68411/issue68411.go") + data, err := os.ReadFile(exportHeader) + if err != nil { + t.Fatal(err) + } + + funcs := []struct{ name, signature string }{ + {"exportFuncWithNoParams", "void exportFuncWithNoParams(void)"}, + {"exportFuncWithParams", "exportFuncWithParams(GoInt a, GoInt b)"}, + } + + var found int + for _, line := range bytes.Split(data, []byte("\n")) { + for _, fn := range funcs { + if bytes.Contains(line, []byte(fn.name)) { + found++ + if !bytes.Contains(line, []byte(fn.signature)) { + t.Errorf("function signature mismatch; got %q, want %q", line, fn.signature) + } + } + } + } + + if found != len(funcs) { + t.Error("missing functions") + } +} diff --git a/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go new file mode 100644 index 00000000000000..a761cc38985bbf --- /dev/null +++ b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +//export exportFuncWithNoParams +func exportFuncWithNoParams() {} + +//export exportFuncWithParams +func exportFuncWithParams(a, b int) {} + +func main() {} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 954c4b70c954eb..d53bbe9cdfde4e 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1012,13 +1012,18 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { s += p.cgoType(fn.Recv.List[0].Type).C.String() s += " recv" } - forFieldList(fntype.Params, - func(i int, aname string, atype ast.Expr) { - if i > 0 || fn.Recv != nil { - s += ", " - } - s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i)) - }) + + if len(fntype.Params.List) > 0 { + forFieldList(fntype.Params, + func(i int, aname string, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + s += ", " + } + s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i)) + }) + } else { + s += "void" + } s += ")" if len(exp.Doc) > 0 {