Skip to content

Commit

Permalink
cmd/cgo: explicitly use void for functions with no parameters
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mauri870 committed Dec 24, 2024
1 parent a9922d0 commit a07bd09
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 7 deletions.
49 changes: 49 additions & 0 deletions src/cmd/cgo/internal/testcshared/cshared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,3 +880,52 @@ 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, err := os.MkdirTemp("", "cshared-TestIssue68411")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)

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)"},
}

scanner := bufio.NewScanner(bytes.NewReader(data))
var found int
for scanner.Scan() {
b := scanner.Bytes()
for _, fn := range funcs {
if bytes.Contains(b, []byte(fn.name)) {
found++
if !bytes.Contains(b, []byte(fn.signature)) {
t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature)
}
}
}
}
if err = scanner.Err(); err != nil {
t.Errorf("scanner encountered error: %v", err)
}
if found != len(funcs) {
t.Error("missing functions")
}
}
15 changes: 15 additions & 0 deletions src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go
Original file line number Diff line number Diff line change
@@ -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() {}
19 changes: 12 additions & 7 deletions src/cmd/cgo/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit a07bd09

Please sign in to comment.