This makes methods on aliases of cgo-generated types a new compiler error.
That is ok because cgo-behavior is not covered by the G1 compatibility
guarantee.
Background: In 2023 we fixed a gopls issue related to this by actually
enabling methods on cgo-generated types in the first place (#59944).
See the discussion in #60725 and this CL for why we believe it is ok
to make this an error now.
Based on a variation of CL 503596 (by Xie Cui).
Fixes #60725.
For #59944.
Change-Id: I7e9e6e1a76447167483a282b268f5183214027c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/629715
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
testenv.MustHaveCGO(t)
// The typechecker should resolve methods declared on aliases of cgo types.
- const src = `
+ const src = `// -gotypesalias=1
+
package p
/*
type Layout = C.struct_layout
-func (l *Layout) Binding() {}
+func (l /* ERROR "cannot define new methods on non-local type Layout" */ *Layout) Binding() {}
func _() {
_ = (*Layout).Binding
"cmd/compile/internal/syntax"
"fmt"
. "internal/types/errors"
+ "path/filepath"
+ "strings"
)
// ----------------------------------------------------------------------------
// as the method."
switch T := atyp.(type) {
case *Named:
- if T.obj.pkg != check.pkg {
+ if T.obj.pkg != check.pkg || isCGoTypeObj(T.obj) {
check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
break
}
check.errorf(recv, InvalidRecv, "invalid receiver type %s", recv.typ)
}
}
+
+// isCGoTypeObj reports whether the given type name was created by cgo.
+func isCGoTypeObj(obj *TypeName) bool {
+ return strings.HasPrefix(obj.name, "_Ctype_") ||
+ strings.HasPrefix(filepath.Base(obj.pos.FileBase().Filename()), "_cgo_")
+}
--- /dev/null
+[short] skip
+[!cgo] skip
+
+# Test that cgo rejects attempts to declare methods
+# on the types A or *A; see issue #60725.
+
+! go build ./a
+stderr 'cannot define new methods on non-local type A'
+stderr 'cannot define new methods on non-local type A'
+
+-- go.mod --
+module example.com
+go 1.24
+
+-- a/a.go --
+package a
+
+/*
+typedef int T;
+*/
+import "C"
+
+type A = C.T
+
+func (A) m1() {}
+func (*A) m2() {}
testenv.MustHaveCGO(t)
// The typechecker should resolve methods declared on aliases of cgo types.
- const src = `
+ const src = `// -gotypesalias=1
+
package p
/*
type Layout = C.struct_layout
-func (l *Layout) Binding() {}
+func (l /* ERROR "cannot define new methods on non-local type Layout" */ *Layout) Binding() {}
func _() {
_ = (*Layout).Binding
"go/ast"
"go/token"
. "internal/types/errors"
+ "path/filepath"
+ "strings"
)
// ----------------------------------------------------------------------------
// as the method."
switch T := atyp.(type) {
case *Named:
- if T.obj.pkg != check.pkg {
+ if T.obj.pkg != check.pkg || isCGoTypeObj(check.fset, T.obj) {
check.errorf(recv, InvalidRecv, "cannot define new methods on non-local type %s", rtyp)
break
}
check.errorf(recv, InvalidRecv, "invalid receiver type %s", recv.typ)
}
}
+
+// isCGoTypeObj reports whether the given type name was created by cgo.
+func isCGoTypeObj(fset *token.FileSet, obj *TypeName) bool {
+ return strings.HasPrefix(obj.name, "_Ctype_") ||
+ strings.HasPrefix(filepath.Base(fset.File(obj.pos).Name()), "_cgo_")
+}