diff --git a/src/cmd/cgo/internal/test/issue44648.go b/src/cmd/cgo/internal/test/issue44648.go new file mode 100644 index 00000000000000..4c320bec4399e8 --- /dev/null +++ b/src/cmd/cgo/internal/test/issue44648.go @@ -0,0 +1,32 @@ +// Copyright 2026 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. + +// Issue 44648. The C wrapper for an exported Go function failed to +// compile if a parameter had the same name as a C type used by the +// function. No runtime test; just make sure it compiles. + +package cgotest + +/* +typedef int issue44648T; +typedef int (*issue44648F)(void *); +*/ +import "C" + +import "unsafe" + +//export Issue44648A +func Issue44648A(issue44648F C.issue44648F, data unsafe.Pointer) C.int { + return 0 +} + +//export Issue44648B +func Issue44648B(issue44648T C.issue44648T) (C.issue44648T, C.issue44648T) { + return issue44648T, issue44648T +} + +//export Issue44648C +func Issue44648C(r C.int) (a, b C.int) { + return r, r +} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 41dfb065e4602e..7b592845d8227e 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1047,25 +1047,35 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { } // Build the wrapper function compiled by gcc. - var s strings.Builder - fmt.Fprintf(&s, "%s %s(", gccResult, exp.ExpName) - if fn.Recv != nil { - s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String()) - s.WriteString(" recv") - } + // The declaration in the header uses the Go parameter + // names, but the definition uses generated names so + // that a parameter cannot shadow a C type or variable + // used in the body of the wrapper (issue 44648). + cdecl := func(paramName func(aname string, i int) string) string { + var s strings.Builder + fmt.Fprintf(&s, "%s %s(", gccResult, exp.ExpName) + if fn.Recv != nil { + s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String()) + s.WriteString(" recv") + } - if len(fntype.Params.List) > 0 { - forFieldList(fntype.Params, - func(i int, aname string, atype ast.Expr) { - if i > 0 || fn.Recv != nil { - s.WriteString(", ") - } - fmt.Fprintf(&s, "%s %s", p.cgoType(atype).C, exportParamName(aname, i)) - }) - } else { - s.WriteString("void") + if len(fntype.Params.List) > 0 { + forFieldList(fntype.Params, + func(i int, aname string, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + s.WriteString(", ") + } + fmt.Fprintf(&s, "%s %s", p.cgoType(atype).C, paramName(aname, i)) + }) + } else { + s.WriteString("void") + } + s.WriteByte(')') + return s.String() + } + genParamName := func(aname string, i int) string { + return fmt.Sprintf("p%d", i) } - s.WriteByte(')') if len(exp.Doc) > 0 { fmt.Fprintf(fgcch, "\n%s", exp.Doc) @@ -1073,11 +1083,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprint(fgcch, "\n") } } - fmt.Fprintf(fgcch, "extern %s;\n", s.String()) + fmt.Fprintf(fgcch, "extern %s;\n", cdecl(exportParamName)) fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD") - fmt.Fprintf(fgcc, "\n%s\n", s.String()) + fmt.Fprintf(fgcc, "\n%s\n", cdecl(genParamName)) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\tsize_t _cgo_ctxt = _cgo_wait_runtime_init_done();\n") // The results part of the argument structure must be @@ -1104,7 +1114,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { } forFieldList(fntype.Params, func(i int, aname string, atype ast.Expr) { - fmt.Fprintf(fgcc, "\t_cgo_a.p%d = %s;\n", i, exportParamName(aname, i)) + fmt.Fprintf(fgcc, "\t_cgo_a.p%d = p%d;\n", i, i) }) fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n") fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &_cgo_a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off)