diff --git a/misc/cgo/testcshared/cshared_test.go b/misc/cgo/testcshared/cshared_test.go index c9e9e5fe63..e4898778be 100644 --- a/misc/cgo/testcshared/cshared_test.go +++ b/misc/cgo/testcshared/cshared_test.go @@ -5,6 +5,7 @@ package cshared_test import ( + "bufio" "bytes" "debug/elf" "debug/pe" @@ -838,3 +839,51 @@ func TestGo2C2Go(t *testing.T) { run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2") runExe(t, runenv, bin) } + +func TestIssue36233(t *testing.T) { + t.Parallel() + + // Test that the export header uses GoComplex64 and GoComplex128 + // for complex types. + + tmpdir, err := os.MkdirTemp("", "cshared-TestIssue36233") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + const exportHeader = "issue36233.h" + + run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue36233/issue36233.go") + data, err := os.ReadFile(exportHeader) + if err != nil { + t.Fatal(err) + } + + funcs := []struct{ name, signature string }{ + {"exportComplex64", "GoComplex64 exportComplex64(GoComplex64 v)"}, + {"exportComplex128", "GoComplex128 exportComplex128(GoComplex128 v)"}, + {"exportComplexfloat", "GoComplex64 exportComplexfloat(GoComplex64 v)"}, + {"exportComplexdouble", "GoComplex128 exportComplexdouble(GoComplex128 v)"}, + } + + 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") + } +} diff --git a/misc/cgo/testcshared/testdata/issue36233/issue36233.go b/misc/cgo/testcshared/testdata/issue36233/issue36233.go new file mode 100644 index 0000000000..d0d1e5d50a --- /dev/null +++ b/misc/cgo/testcshared/testdata/issue36233/issue36233.go @@ -0,0 +1,29 @@ +// Copyright 2022 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 + +// #include +import "C" + +//export exportComplex64 +func exportComplex64(v complex64) complex64 { + return v +} + +//export exportComplex128 +func exportComplex128(v complex128) complex128 { + return v +} + +//export exportComplexfloat +func exportComplexfloat(v C.complexfloat) C.complexfloat { + return v +} + +//export exportComplexdouble +func exportComplexdouble(v C.complexdouble) C.complexdouble { + return v +} + +func main() {} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 8ead173e64..adbb761e38 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1399,6 +1399,19 @@ func (p *Package) cgoType(e ast.Expr) *Type { case *ast.ChanType: return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoChan")} case *ast.Ident: + goTypesFixup := func(r *Type) *Type { + if r.Size == 0 { // int or uint + rr := new(Type) + *rr = *r + rr.Size = p.IntSize + rr.Align = p.IntSize + r = rr + } + if r.Align > p.PtrSize { + r.Align = p.PtrSize + } + return r + } // Look up the type in the top level declarations. // TODO: Handle types defined within a function. for _, d := range p.Decl { @@ -1417,6 +1430,17 @@ func (p *Package) cgoType(e ast.Expr) *Type { } } if def := typedef[t.Name]; def != nil { + if defgo, ok := def.Go.(*ast.Ident); ok { + switch defgo.Name { + case "complex64", "complex128": + // MSVC does not support the _Complex keyword + // nor the complex macro. + // Use GoComplex64 and GoComplex128 instead, + // which are typedef-ed to a compatible type. + // See go.dev/issues/36233. + return goTypesFixup(goTypes[defgo.Name]) + } + } return def } if t.Name == "uintptr" { @@ -1430,17 +1454,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} } if r, ok := goTypes[t.Name]; ok { - if r.Size == 0 { // int or uint - rr := new(Type) - *rr = *r - rr.Size = p.IntSize - rr.Align = p.IntSize - r = rr - } - if r.Align > p.PtrSize { - r.Align = p.PtrSize - } - return r + return goTypesFixup(r) } error_(e.Pos(), "unrecognized Go type %s", t.Name) return &Type{Size: 4, Align: 4, C: c("int")} @@ -1895,8 +1909,14 @@ typedef GoUintGOINTBITS GoUint; typedef size_t GoUintptr; typedef float GoFloat32; typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; +#endif /* static assertion to make sure the file is being used on architecture