From abfe470087a231d3605f2bc0eb7ef4eea6726b87 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 17 Nov 2025 10:25:40 +0100 Subject: [PATCH] runtime/cgo: add support for `any` param and return type When using `any` as param or return type of an exported function, we currently have the error `unrecognized Go type any`. `any` is an alias of `interface{}` which is already supported. This would avoid such change: https://github.com/php/frankenphp/pull/1976 Fixes #76340 --- src/cmd/cgo/gcc.go | 3 +++ src/cmd/cgo/internal/test/cgo_test.go | 1 + src/cmd/cgo/internal/test/test.go | 31 +++++++++++++++++++++++++++ src/cmd/cgo/internal/test/testx.go | 18 ++++++++++++++++ src/cmd/cgo/out.go | 3 +++ src/runtime/cgocall.go | 3 +++ 6 files changed, 59 insertions(+) diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index d3de3906b48eb0..300ccae350b44d 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -1121,6 +1121,9 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool { if t.Name == "error" { return true } + if t.Name == "any" { + return true + } if goTypes[t.Name] != nil { return false } diff --git a/src/cmd/cgo/internal/test/cgo_test.go b/src/cmd/cgo/internal/test/cgo_test.go index 5393552e07a4d1..04e06cf95ec550 100644 --- a/src/cmd/cgo/internal/test/cgo_test.go +++ b/src/cmd/cgo/internal/test/cgo_test.go @@ -106,6 +106,7 @@ func TestSetEnv(t *testing.T) { testSetEnv(t) } func TestThreadLock(t *testing.T) { testThreadLockFunc(t) } func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } +func Test76340(t *testing.T) { test76340(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } func BenchmarkGoString(b *testing.B) { benchGoString(b) } diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go index 9626407d882ef2..958dfc248e659a 100644 --- a/src/cmd/cgo/internal/test/test.go +++ b/src/cmd/cgo/internal/test/test.go @@ -953,6 +953,18 @@ typedef struct { } issue69086struct; static int issue690861(issue69086struct* p) { p->b = 1234; return p->c; } static int issue690862(unsigned long ul1, unsigned long ul2, unsigned int u, issue69086struct s) { return (int)(s.b); } + +typedef struct { void *t; void *v; } GoInterface; +extern int exportAny76340Param(GoInterface); +extern GoInterface exportAny76340Return(int); + +int issue76340testFromC(GoInterface obj) { + return exportAny76340Param(obj); +} + +GoInterface issue76340returnFromC(int val) { + return exportAny76340Return(val); +} */ import "C" @@ -2396,3 +2408,22 @@ func test69086(t *testing.T) { t.Errorf("call: got %d, want 1234", got) } } + +// Issue 76340. +func test76340(t *testing.T) { + var emptyInterface C.GoInterface + r1 := C.issue76340testFromC(emptyInterface) + if r1 != 0 { + t.Errorf("issue76340testFromC with nil interface: got %d, want 0", r1) + } + + r2 := C.issue76340returnFromC(42) + if r2.t == nil && r2.v == nil { + t.Error("issue76340returnFromC(42) returned nil interface") + } + + r3 := C.issue76340returnFromC(0) + if r3.t != nil || r3.v != nil { + t.Errorf("issue76340returnFromC(0) returned non-nil interface: got %v, want nil", r3) + } +} diff --git a/src/cmd/cgo/internal/test/testx.go b/src/cmd/cgo/internal/test/testx.go index 9a63b9e10087ad..21ba52260ef893 100644 --- a/src/cmd/cgo/internal/test/testx.go +++ b/src/cmd/cgo/internal/test/testx.go @@ -595,3 +595,21 @@ func test49633(t *testing.T) { t.Errorf("msg = %q, want 'hello'", v.msg) } } + +//export exportAny76340Param +func exportAny76340Param(obj any) C.int { + if obj == nil { + return 0 + } + + return 1 +} + +//export exportAny76340Return +func exportAny76340Return(val C.int) any { + if val == 0 { + return nil + } + + return int(val) +} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 701a8530ffc984..d6406b5b8a422c 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1528,6 +1528,9 @@ func (p *Package) doCgoType(e ast.Expr, m map[ast.Expr]bool) *Type { if t.Name == "error" { return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} } + if t.Name == "any" { + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} + } if r, ok := goTypes[t.Name]; ok { return goTypesFixup(r) } diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index a53fd6da340190..737aea930acca3 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -795,6 +795,9 @@ func cgoCheckResult(val any) { ep := efaceOf(&val) t := ep._type + if t == nil { + return + } cgoCheckArg(t, ep.data, !t.IsDirectIface(), false, cgoResultFail) }