mirror of
https://github.com/golang/go.git
synced 2026-04-03 09:49:56 +09:00
cmd/compile, go/*: allow generic methods starting with Go 1.27
This only changes the type checkers and tests. Backend compiler changes are not included. For #77273. Change-Id: I8cf0b6fddf6afd6b08b06ba6fdf9c726af4bea8d Reviewed-on: https://go-review.googlesource.com/c/go/+/746820 Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Mark Freeman <markfreeman@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
committed by
Gopher Robot
parent
d4ace6b007
commit
e84da0405b
@@ -415,22 +415,27 @@ func (check *Checker) collectObjects() {
|
||||
|
||||
case *syntax.FuncDecl:
|
||||
name := s.Name.Value
|
||||
obj := NewFunc(s.Name.Pos(), pkg, name, nil)
|
||||
hasTParamError := false // avoid duplicate type parameter errors
|
||||
obj := NewFunc(s.Name.Pos(), pkg, name, nil) // signature set later
|
||||
var tparam0 *syntax.Field
|
||||
if len(s.TParamList) > 0 {
|
||||
tparam0 = s.TParamList[0]
|
||||
}
|
||||
if s.Recv == nil {
|
||||
// regular function
|
||||
if name == "init" || name == "main" && pkg.name == "main" {
|
||||
// init and main functions must not declare type and ordinary parameters or results
|
||||
code := InvalidInitDecl
|
||||
if name == "main" {
|
||||
code = InvalidMainDecl
|
||||
}
|
||||
if len(s.TParamList) != 0 {
|
||||
check.softErrorf(s.TParamList[0], code, "func %s must have no type parameters", name)
|
||||
hasTParamError = true
|
||||
if tparam0 != nil {
|
||||
check.softErrorf(tparam0, code, "func %s must have no type parameters", name)
|
||||
}
|
||||
if t := s.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
|
||||
check.softErrorf(s.Name, code, "func %s must have no arguments and no return values", name)
|
||||
}
|
||||
} else {
|
||||
_ = tparam0 != nil && check.verifyVersionf(tparam0, go1_18, "type parameter")
|
||||
}
|
||||
// don't declare init functions in the package scope - they are invisible
|
||||
if name == "init" {
|
||||
@@ -452,14 +457,9 @@ func (check *Checker) collectObjects() {
|
||||
if recv, _ := base.(*syntax.Name); recv != nil && name != "_" {
|
||||
methods = append(methods, methodInfo{obj, ptr, recv})
|
||||
}
|
||||
// methods cannot have type parameters for now
|
||||
if len(s.TParamList) != 0 {
|
||||
check.softErrorf(s.TParamList[0], InvalidMethodTypeParams, "method %s must have no type parameters", name)
|
||||
hasTParamError = true
|
||||
}
|
||||
_ = tparam0 != nil && check.verifyVersionf(tparam0, go1_27, "generic method")
|
||||
check.recordDef(s.Name, obj)
|
||||
}
|
||||
_ = len(s.TParamList) != 0 && !hasTParamError && check.verifyVersionf(s.TParamList[0], go1_18, "type parameter")
|
||||
info := &declInfo{file: fileScope, version: check.version, fdecl: s}
|
||||
// Methods are not package-level objects but we still track them in the
|
||||
// object map so that we can handle them like regular functions (if the
|
||||
|
||||
@@ -44,6 +44,7 @@ var (
|
||||
go1_22 = asGoVersion("go1.22")
|
||||
go1_23 = asGoVersion("go1.23")
|
||||
go1_26 = asGoVersion("go1.26")
|
||||
go1_27 = asGoVersion("go1.27")
|
||||
|
||||
// current (deployed) Go version
|
||||
go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
|
||||
|
||||
@@ -400,7 +400,10 @@ func (check *Checker) collectObjects() {
|
||||
case funcDecl:
|
||||
name := d.decl.Name.Name
|
||||
obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil) // signature set later
|
||||
hasTParamError := false // avoid duplicate type parameter errors
|
||||
var tparam0 *ast.Field
|
||||
if d.decl.Type.TypeParams.NumFields() > 0 {
|
||||
tparam0 = d.decl.Type.TypeParams.List[0]
|
||||
}
|
||||
if d.decl.Recv.NumFields() == 0 {
|
||||
// regular function
|
||||
if d.decl.Recv != nil {
|
||||
@@ -408,18 +411,20 @@ func (check *Checker) collectObjects() {
|
||||
// treat as function
|
||||
}
|
||||
if name == "init" || (name == "main" && check.pkg.name == "main") {
|
||||
// init and main functions must not declare type and ordinary parameters or results
|
||||
code := InvalidInitDecl
|
||||
if name == "main" {
|
||||
code = InvalidMainDecl
|
||||
}
|
||||
if d.decl.Type.TypeParams.NumFields() != 0 {
|
||||
check.softErrorf(d.decl.Type.TypeParams.List[0], code, "func %s must have no type parameters", name)
|
||||
hasTParamError = true
|
||||
if tparam0 != nil {
|
||||
check.softErrorf(tparam0, code, "func %s must have no type parameters", name)
|
||||
}
|
||||
if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
|
||||
// TODO(rFindley) Should this be a hard error?
|
||||
check.softErrorf(d.decl.Name, code, "func %s must have no arguments and no return values", name)
|
||||
}
|
||||
} else {
|
||||
_ = tparam0 != nil && check.verifyVersionf(tparam0, go1_18, "type parameter")
|
||||
}
|
||||
if name == "init" {
|
||||
// don't declare init functions in the package scope - they are invisible
|
||||
@@ -433,12 +438,6 @@ func (check *Checker) collectObjects() {
|
||||
}
|
||||
} else {
|
||||
// method
|
||||
|
||||
// TODO(rFindley) earlier versions of this code checked that methods
|
||||
// have no type parameters, but this is checked later
|
||||
// when type checking the function type. Confirm that
|
||||
// we don't need to check tparams here.
|
||||
|
||||
ptr, base, _ := check.unpackRecv(d.decl.Recv.List[0].Type, false)
|
||||
// (Methods with invalid receiver cannot be associated to a type, and
|
||||
// methods with blank _ names are never found; no need to collect any
|
||||
@@ -446,14 +445,9 @@ func (check *Checker) collectObjects() {
|
||||
if recv, _ := base.(*ast.Ident); recv != nil && name != "_" {
|
||||
methods = append(methods, methodInfo{obj, ptr, recv})
|
||||
}
|
||||
// methods cannot have type parameters for now
|
||||
if d.decl.Type.TypeParams.NumFields() != 0 {
|
||||
check.softErrorf(d.decl.Type.TypeParams.List[0], InvalidMethodTypeParams, "method %s must have no type parameters", name)
|
||||
hasTParamError = true
|
||||
}
|
||||
_ = tparam0 != nil && check.verifyVersionf(tparam0, go1_27, "generic method")
|
||||
check.recordDef(d.decl.Name, obj)
|
||||
}
|
||||
_ = d.decl.Type.TypeParams.NumFields() != 0 && !hasTParamError && check.verifyVersionf(d.decl.Type.TypeParams.List[0], go1_18, "type parameter")
|
||||
info := &declInfo{file: fileScope, version: check.version, fdecl: d.decl}
|
||||
// Methods are not package-level objects but we still track them in the
|
||||
// object map so that we can handle them like regular functions (if the
|
||||
|
||||
@@ -44,6 +44,7 @@ var (
|
||||
go1_22 = asGoVersion("go1.22")
|
||||
go1_23 = asGoVersion("go1.23")
|
||||
go1_26 = asGoVersion("go1.26")
|
||||
go1_27 = asGoVersion("go1.27")
|
||||
|
||||
// current (deployed) Go version
|
||||
go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
|
||||
|
||||
@@ -326,11 +326,13 @@ func init() {}
|
||||
func init[_ /* ERROR "func init must have no type parameters" */ any]() {}
|
||||
func init[P /* ERROR "func init must have no type parameters" */ any]() {}
|
||||
|
||||
// type parameters on concrete methods are permitted
|
||||
|
||||
type T struct {}
|
||||
|
||||
func (T) m1() {}
|
||||
func (T) m2[_ /* ERROR "method m2 must have no type parameters" */ any]() {}
|
||||
func (T) m3[P /* ERROR "method m3 must have no type parameters" */ any]() {}
|
||||
func (T) m2[_ any]() {}
|
||||
func (T) m3[P any]() {}
|
||||
|
||||
// type inference across parameterized types
|
||||
|
||||
|
||||
27
src/internal/types/testdata/fixedbugs/issue50427a.go
vendored
Normal file
27
src/internal/types/testdata/fixedbugs/issue50427a.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// -lang=go1.26
|
||||
|
||||
// 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 p
|
||||
|
||||
// The parser does not accept type parameters for interface methods.
|
||||
// In the past, type checking the code below led to a crash (#50427).
|
||||
|
||||
type T interface{ m[ /* ERROR "must have no type parameters" */ P any]() }
|
||||
|
||||
func _(t T) {
|
||||
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = t /* ERROR "does not implement" */
|
||||
}
|
||||
|
||||
// Type parameters on concrete methods are not permitted before Go 1.27.
|
||||
|
||||
type S struct{}
|
||||
|
||||
func (S) m[P /* ERROR "generic method requires go1.27 or later" */ any]() {}
|
||||
|
||||
func _(s S) {
|
||||
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s /* ERROR "does not implement" */
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
package p
|
||||
|
||||
// The parser no longer parses type parameters for methods.
|
||||
// The parser does not accept type parameters for interface methods.
|
||||
// In the past, type checking the code below led to a crash (#50427).
|
||||
|
||||
type T interface{ m[ /* ERROR "must have no type parameters" */ P any]() }
|
||||
@@ -13,9 +13,11 @@ func _(t T) {
|
||||
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = t /* ERROR "does not implement" */
|
||||
}
|
||||
|
||||
// Type parameters on concrete methods are permitted as of Go 1.27.
|
||||
|
||||
type S struct{}
|
||||
|
||||
func (S) m[P /* ERROR "must have no type parameters" */ any]() {}
|
||||
func (S) m[P any]() {}
|
||||
|
||||
func _(s S) {
|
||||
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s /* ERROR "does not implement" */
|
||||
105
src/internal/types/testdata/spec/methods.go
vendored
Normal file
105
src/internal/types/testdata/spec/methods.go
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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.
|
||||
|
||||
package p
|
||||
|
||||
// Non-interface methods may declare type parameters.
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) m[P any](x P) P { return x }
|
||||
|
||||
func _() {
|
||||
// A generic method must be instantiated before it is called.
|
||||
var x T
|
||||
var _ int = x.m[int](1) // explicit instantiation
|
||||
var _ int = x.m(2) // instantiation via type inference
|
||||
var _ int = x /* ERROR "cannot use x.m(3.14) (value of type float64)" */ .m(3.14)
|
||||
|
||||
// Receivers of generic method calls may be complex expressions:
|
||||
// Instantiation must work not just on simple operands.
|
||||
var a [10]T
|
||||
_ = a[0].m[int] // explicit instantiation
|
||||
_ = a[1].m(2.72) // instantiation via type inference
|
||||
|
||||
var m map[string][]struct{ T }
|
||||
_ = m["foo"][0].T.m[float32]
|
||||
_ = m["foo"][0].T.m(2.72)
|
||||
|
||||
_ = m["foo"][0].m[float32] // method promotion with explicit instantiation
|
||||
_ = m["foo"][0].m(2.72) // method promotion with instantiation via type inference
|
||||
|
||||
// A generic method expression may be assigned to a function after instantiation.
|
||||
var _ func(T, int) int = T.m[int] // explicit instantiation
|
||||
var _ func(T, int) int = T.m // instantiation via type inference
|
||||
|
||||
// A generic method value may be assigned to a function after instantiation.
|
||||
var _ func(int) int = x.m[int] // explicit instantiation
|
||||
var _ func(int) int = x.m // instantiation via type inference
|
||||
}
|
||||
|
||||
// Generic methods may be added to generic types.
|
||||
type G[F any] struct {
|
||||
f F
|
||||
}
|
||||
|
||||
// The constraint for the method parameter may refer to the receiver type parameter.
|
||||
func (g G[F]) m[H interface{ convert(F) H }]() (r H) {
|
||||
return r.convert(g.f)
|
||||
}
|
||||
|
||||
// But the usual restrictions for type terms still apply.
|
||||
func (G[F]) m2[P F /* ERROR "cannot use a type parameter as constraint" */ ]() {}
|
||||
func (G[F]) m3[P *F]() {} // this is ok
|
||||
|
||||
// Generic methods don't satisfy interfaces.
|
||||
type I[P any] interface {
|
||||
m(P) P
|
||||
}
|
||||
|
||||
var _ I[int] = T /* ERROR "(wrong type for method m)\n\t\thave m[P any](P) P\n\t\twant m(int) int" */ {}
|
||||
|
||||
// A method declaring type parameters is generic even if it doesn't use the type parameters in its signature.
|
||||
type U struct {}
|
||||
|
||||
func (U) m[_ any](x int) int { return x }
|
||||
|
||||
var _ I[int] = U /* ERROR "wrong type for method m)\n\t\thave m[_ any](int) int\n\t\twant m(int) int" */ {}
|
||||
|
||||
type J interface {
|
||||
m()
|
||||
}
|
||||
|
||||
type V struct {}
|
||||
|
||||
func (V) m[_ any]() {}
|
||||
|
||||
var _ J = V /* ERROR "wrong type for method m)\n\t\thave m[_ any]()\n\t\twant m()" */ {}
|
||||
|
||||
// Test case from parser smoke test.
|
||||
|
||||
type List[E any] []E
|
||||
|
||||
func (l List[E]) Map[F any](m func(E) F) (r List[F]) {
|
||||
for _, x := range l {
|
||||
r = append(r, m(x))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _() {
|
||||
l := List[string]{"foo", "foobar", "42"}
|
||||
r := l.Map(func(s string) int { return len(s)})
|
||||
_ = r
|
||||
}
|
||||
|
||||
func _[E, F any](l List[E]) List[F] {
|
||||
var f func(List[E], func(E) F) List[F] = List[E].Map // method expression & type inference
|
||||
return f(l, func(E) F { var f F; return f })
|
||||
}
|
||||
|
||||
func _[E, F any](l List[E]) List[F] {
|
||||
var f func(func(E) F) List[F] = l.Map // method value & type inference
|
||||
return f(func(E) F { var f F; return f })
|
||||
}
|
||||
@@ -8,7 +8,7 @@ package p
|
||||
|
||||
type S struct{}
|
||||
|
||||
func (S) _[_ any]() {} // ERROR "method _ must have no type parameters"
|
||||
func (S) _[_ any]() {}
|
||||
|
||||
type _ interface {
|
||||
m[_ any]() // ERROR "interface method must have no type parameters"
|
||||
|
||||
Reference in New Issue
Block a user