aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Griesemer <gri@google.com>2026-01-22 16:16:17 -0800
committerRobert Griesemer <gri@google.com>2026-02-19 12:22:13 -0800
commit06dc5db75d4c2548c0187f34ce79389678be7ca0 (patch)
tree67ed553c81ea149812d0d2482ec4d2dafe216fcf /src
parenta8032d4c781f14fa0bd561d96e719492aee08c23 (diff)
downloadgo-06dc5db75d4c2548c0187f34ce79389678be7ca0.tar.xz
cmd/compile, go/*: move method type parameter checks from parsers to type checkers
The parsers (cmd/compile/internal/syntax and go/parser) always accepted type parameters on methods for parser robustness but reported an error. With this change, the parsers accept the type parameters on methods, and then the type checkers (cmd/compile/internal/types2 and go/types) complain about them. Add test case for method type parameters when running the parsers only. Adjust some existing test cases as needed. This change is a necessary first step towards implementing generic methods but does not enable them. For #77273. Change-Id: I291fcf0aef0c917c74b32131c88b9e4ed71c5060 Reviewed-on: https://go-review.googlesource.com/c/go/+/738441 Reviewed-by: Mark Freeman <markfreeman@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Griesemer <gri@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/compile/internal/syntax/parser.go8
-rw-r--r--src/cmd/compile/internal/syntax/testdata/smoketest.go30
-rw-r--r--src/cmd/compile/internal/types2/resolver.go5
-rw-r--r--src/cmd/compile/internal/types2/signature.go1
-rw-r--r--src/go/parser/parser.go6
-rw-r--r--src/go/parser/short_test.go6
-rw-r--r--src/go/parser/testdata/issue50427.go22
-rw-r--r--src/go/types/interface.go4
-rw-r--r--src/go/types/resolver.go5
-rw-r--r--src/go/types/signature.go6
-rw-r--r--src/internal/types/testdata/check/typeparams.go4
-rw-r--r--src/internal/types/testdata/fixedbugs/issue50427.go2
12 files changed, 53 insertions, 26 deletions
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index 8278685943..aade7b9fd3 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -797,9 +797,9 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
f.pos = p.pos()
f.Pragma = p.takePragma()
- var context string
+ hasRecv := false
if p.got(_Lparen) {
- context = "method"
+ hasRecv = true
rcvr := p.paramList(nil, nil, _Rparen, false, false)
switch len(rcvr) {
case 0:
@@ -814,13 +814,13 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
if p.tok == _Name {
f.Name = p.name()
- f.TParamList, f.Type = p.funcType(context)
+ f.TParamList, f.Type = p.funcType("")
} else {
f.Name = NewName(p.pos(), "_")
f.Type = new(FuncType)
f.Type.pos = p.pos()
msg := "expected name or ("
- if context != "" {
+ if hasRecv {
msg = "expected name"
}
p.syntaxError(msg)
diff --git a/src/cmd/compile/internal/syntax/testdata/smoketest.go b/src/cmd/compile/internal/syntax/testdata/smoketest.go
index 6b3593ac7a..e7c9f7283f 100644
--- a/src/cmd/compile/internal/syntax/testdata/smoketest.go
+++ b/src/cmd/compile/internal/syntax/testdata/smoketest.go
@@ -71,3 +71,33 @@ type _ interface {
T[P]
T[P1, P2]
}
+
+// generic method
+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 })
+}
+
+// disallowed type parameters
+
+type _ func /* ERROR function type must have no type parameters */ [P any](P)
+type _ interface {
+ m /* ERROR interface method must have no type parameters */ [P any](P)
+}
+
+var _ = func /* ERROR function type must have no type parameters */ [P any](P) {}
diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go
index f075f5447f..0911ae9c90 100644
--- a/src/cmd/compile/internal/types2/resolver.go
+++ b/src/cmd/compile/internal/types2/resolver.go
@@ -452,6 +452,11 @@ 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
+ }
check.recordDef(s.Name, obj)
}
_ = len(s.TParamList) != 0 && !hasTParamError && check.verifyVersionf(s.TParamList[0], go1_18, "type parameter")
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
index d569ba8013..9d739909fd 100644
--- a/src/cmd/compile/internal/types2/signature.go
+++ b/src/cmd/compile/internal/types2/signature.go
@@ -155,7 +155,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
// collect and declare function type parameters
if tparams != nil {
- // The parser will complain about invalid type parameters for methods.
check.collectTypeParams(&sig.tparams, tparams)
}
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index abf5cba6d3..30cac22379 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -2794,12 +2794,6 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
var tparams *ast.FieldList
if p.tok == token.LBRACK {
tparams = p.parseTypeParameters()
- if recv != nil && tparams != nil {
- // Method declarations do not have type parameters. We parse them for a
- // better error message and improved error recovery.
- p.error(tparams.Opening, "method must have no type parameters")
- tparams = nil
- }
}
params := p.parseParameters(false)
results := p.parseParameters(true)
diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go
index 422d1b38c3..e02f7f46b0 100644
--- a/src/go/parser/short_test.go
+++ b/src/go/parser/short_test.go
@@ -207,9 +207,9 @@ var invalids = []string{
`package p; func _[type /* ERROR "found 'type'" */ P, *Q interface{}]()`,
- `package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B any](a A) B`,
- `package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B C](a A) B`,
- `package p; func (T) _[ /* ERROR "must have no type parameters" */ A, B C[A, B]](a A) B`,
+ `package p; func (T) _[A, B any](a A) B`,
+ `package p; func (T) _[A, B C](a A) B`,
+ `package p; func (T) _[A, B C[A, B]](a A) B`,
`package p; func(*T[e, e /* ERROR "e redeclared" */ ]) _()`,
diff --git a/src/go/parser/testdata/issue50427.go2 b/src/go/parser/testdata/issue50427.go2
index 15214594e2..f810f15e81 100644
--- a/src/go/parser/testdata/issue50427.go2
+++ b/src/go/parser/testdata/issue50427.go2
@@ -12,7 +12,7 @@ func _(t T) {
type S struct{}
-func (S) m[ /* ERROR "must have no type parameters" */ P any]() {}
+func (S) m[P any]() {}
func _(s S) {
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s
diff --git a/src/go/types/interface.go b/src/go/types/interface.go
index 5f9c88d8f5..012b20c577 100644
--- a/src/go/types/interface.go
+++ b/src/go/types/interface.go
@@ -188,13 +188,13 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
continue // ignore
}
- // The go/parser doesn't accept method type parameters but an ast.FuncType may have them.
+ // The go/parser doesn't accept interface method type parameters but an ast.FuncType may have them.
if sig.tparams != nil {
var at positioner = f.Type
if ftyp, _ := f.Type.(*ast.FuncType); ftyp != nil && ftyp.TypeParams != nil {
at = ftyp.TypeParams
}
- check.error(at, InvalidSyntaxTree, "methods cannot have type parameters")
+ check.error(at, InvalidSyntaxTree, "interface methods cannot have type parameters")
}
// use named receiver type if available (for better error messages)
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 4f7dfc19fd..7efcac7486 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -446,6 +446,11 @@ 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
+ }
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")
diff --git a/src/go/types/signature.go b/src/go/types/signature.go
index 13f60c0772..930d70950b 100644
--- a/src/go/types/signature.go
+++ b/src/go/types/signature.go
@@ -173,12 +173,6 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
// collect and declare function type parameters
if ftyp.TypeParams != nil {
- // Always type-check method type parameters but complain that they are not allowed.
- // (A separate check is needed when type-checking interface method signatures because
- // they don't have a receiver specification.)
- if recvPar != nil {
- check.error(ftyp.TypeParams, InvalidMethodTypeParams, "methods cannot have type parameters")
- }
check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
}
diff --git a/src/internal/types/testdata/check/typeparams.go b/src/internal/types/testdata/check/typeparams.go
index b73f1fee6d..bbe448cb96 100644
--- a/src/internal/types/testdata/check/typeparams.go
+++ b/src/internal/types/testdata/check/typeparams.go
@@ -329,8 +329,8 @@ func init[P /* ERROR "func init must have no type parameters" */ any]() {}
type T struct {}
func (T) m1() {}
-func (T) m2[ /* ERROR "method must have no type parameters" */ _ any]() {}
-func (T) m3[ /* ERROR "method must have no type parameters" */ P any]() {}
+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]() {}
// type inference across parameterized types
diff --git a/src/internal/types/testdata/fixedbugs/issue50427.go b/src/internal/types/testdata/fixedbugs/issue50427.go
index d89d63e308..6cc8e4178a 100644
--- a/src/internal/types/testdata/fixedbugs/issue50427.go
+++ b/src/internal/types/testdata/fixedbugs/issue50427.go
@@ -15,7 +15,7 @@ func _(t T) {
type S struct{}
-func (S) m[ /* ERROR "must have no type parameters" */ P any]() {}
+func (S) m[P /* ERROR "must have no type parameters" */ any]() {}
func _(s S) {
var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s /* ERROR "does not implement" */