diff options
| author | Robert Griesemer <gri@google.com> | 2026-01-22 16:16:17 -0800 |
|---|---|---|
| committer | Robert Griesemer <gri@google.com> | 2026-02-19 12:22:13 -0800 |
| commit | 06dc5db75d4c2548c0187f34ce79389678be7ca0 (patch) | |
| tree | 67ed553c81ea149812d0d2482ec4d2dafe216fcf /src | |
| parent | a8032d4c781f14fa0bd561d96e719492aee08c23 (diff) | |
| download | go-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.go | 8 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/testdata/smoketest.go | 30 | ||||
| -rw-r--r-- | src/cmd/compile/internal/types2/resolver.go | 5 | ||||
| -rw-r--r-- | src/cmd/compile/internal/types2/signature.go | 1 | ||||
| -rw-r--r-- | src/go/parser/parser.go | 6 | ||||
| -rw-r--r-- | src/go/parser/short_test.go | 6 | ||||
| -rw-r--r-- | src/go/parser/testdata/issue50427.go2 | 2 | ||||
| -rw-r--r-- | src/go/types/interface.go | 4 | ||||
| -rw-r--r-- | src/go/types/resolver.go | 5 | ||||
| -rw-r--r-- | src/go/types/signature.go | 6 | ||||
| -rw-r--r-- | src/internal/types/testdata/check/typeparams.go | 4 | ||||
| -rw-r--r-- | src/internal/types/testdata/fixedbugs/issue50427.go | 2 |
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" */ |
