]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/go: cgo export header to be compatible with MSVC complex types
authorqmuntal <quimmuntal@gmail.com>
Thu, 31 Mar 2022 12:15:22 +0000 (14:15 +0200)
committerGopher Robot <gobot@golang.org>
Fri, 8 Apr 2022 14:13:32 +0000 (14:13 +0000)
After CL 379474 has landed, the only remaining cgo export header
incompatibility with MSVC is the use of the _Complex macro,
which is not supported in MSVC even when it is part of the ISO C99
standard (1).

Since MSVC 2015 (2), complex math are supported via _Fcomplex and
_Dcomplex, which are equivalent to float _Complex and double _Complex.

As MSVC and C complex types have the same memory layout, we should
be able to typedef GoComplex64 and GoComplex128 to the appropriate
type in MSVC.

It is important to note that this CL is not adding MSVC support to cgo.
C compilers should still be GCC-compatible.

This CL is about allowing to include, without further modifications,
a DLL export header generated by cgo, normally using Mingw-W64 compiler,
into a MSVC project. This was already possible if the export header
changes introduced in this CL were done outside cgo, either manually or
in a post-build script.

Fixes #36233

1: https://docs.microsoft.com/en-us/cpp/c-runtime-library/complex-math-support
2: https://docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?c-standard-library-features-1
Change-Id: Iad8f26984b115c728e3b73f3a8334ade7a11cfa1
Reviewed-on: https://go-review.googlesource.com/c/go/+/397134
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Cherry Mui <cherryyz@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
Auto-Submit: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

misc/cgo/testcshared/cshared_test.go
misc/cgo/testcshared/testdata/issue36233/issue36233.go [new file with mode: 0644]
src/cmd/cgo/out.go

index c9e9e5fe63e4a53222b442c3db6f846a853d3b0d..e4898778be80ed9c9ea07d205b064bfd086be070 100644 (file)
@@ -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 (file)
index 0000000..d0d1e5d
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2022 The Go Authors. All rights reserved.\r
+// Use of this source code is governed by a BSD-style\r
+// license that can be found in the LICENSE file.\r
+package main\r
+\r
+// #include <complex.h>\r
+import "C"\r
+\r
+//export exportComplex64\r
+func exportComplex64(v complex64) complex64 {\r
+       return v\r
+}\r
+\r
+//export exportComplex128\r
+func exportComplex128(v complex128) complex128 {\r
+       return v\r
+}\r
+\r
+//export exportComplexfloat\r
+func exportComplexfloat(v C.complexfloat) C.complexfloat {\r
+       return v\r
+}\r
+\r
+//export exportComplexdouble\r
+func exportComplexdouble(v C.complexdouble) C.complexdouble {\r
+       return v\r
+}\r
+\r
+func main() {}\r
index 8ead173e64d865baed19f91e15daeb77d3654872..adbb761e3887117062861e31ac5a7b91bf95efa6 100644 (file)
@@ -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 <complex.h>
+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