go/types, types2: method recv type parameter count must match base type parameter count

Check receiver type parameter count when type checking the method
signature and report a suitable error (don't rely on delayed
instantiation and possibly constraint type inference).

While at it, simplify blank name recoding and type bound rewriting.

Stop-gap measure to avoid crashes in the compiler.

Fixes #51339.
For #51343.

Change-Id: Idbe2d32d69b66573ca973339f8924b349d2bc9cc
Reviewed-on: https://go-review.googlesource.com/c/go/+/387836
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2022-02-23 21:43:06 -08:00
parent c15527f0b0
commit c0840a7c72
6 changed files with 80 additions and 49 deletions

View file

@ -294,15 +294,14 @@ func (check *Checker) typesSummary(list []Type, variadic bool) string {
return "(" + strings.Join(res, ", ") + ")"
}
func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
measure := func(x int, unit string) string {
s := fmt.Sprintf("%d %s", x, unit)
if x != 1 {
s += "s"
}
return s
func measure(x int, unit string) string {
if x != 1 {
unit += "s"
}
return fmt.Sprintf("%d %s", x, unit)
}
func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
vars := measure(nvars, "variable")
vals := measure(nvals, "value")
rhs0 := rhs[0]

View file

@ -116,11 +116,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
// lookup in the scope.
for i, p := range rparams {
if p.Value == "_" {
tpar := sig.rparams.At(i)
if check.recvTParamMap == nil {
check.recvTParamMap = make(map[*syntax.Name]*TypeParam)
}
check.recvTParamMap[p] = tpar
check.recvTParamMap[p] = tparams[i]
}
}
// determine receiver type to get its type parameters
@ -136,22 +135,23 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
}
}
// provide type parameter bounds
// - only do this if we have the right number (otherwise an error is reported elsewhere)
if sig.RecvTypeParams().Len() == len(recvTParams) {
// We have a list of *TypeNames but we need a list of Types.
list := make([]Type, sig.RecvTypeParams().Len())
for i, t := range sig.RecvTypeParams().list() {
list[i] = t
check.mono.recordCanon(t, recvTParams[i])
}
smap := makeSubstMap(recvTParams, list)
for i, tpar := range sig.RecvTypeParams().list() {
bound := recvTParams[i].bound
// bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the
// current context.
tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil)
if len(tparams) == len(recvTParams) {
smap := makeRenameMap(recvTParams, tparams)
for i, tpar := range tparams {
recvTPar := recvTParams[i]
check.mono.recordCanon(tpar, recvTPar)
// recvTPar.bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the current
// context.
tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil)
}
} else if len(tparams) < len(recvTParams) {
// Reporting an error here is a stop-gap measure to avoid crashes in the
// compiler when a type parameter/argument cannot be inferred later. It
// may lead to follow-on errors (see issues #51339, #51343).
// TODO(gri) find a better solution
got := measure(len(tparams), "type parameter")
check.errorf(recvPar, "got %s, but receiver base type declares %d", got, len(recvTParams))
}
}
}

View file

@ -0,0 +1,16 @@
// 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.
// This file is tested when running "go test -run Manual"
// without source arguments. Use for one-off debugging.
package p
type T[P any, B *P] struct{}
func (T /* ERROR cannot use generic type */ ) m0() {}
func (T /* ERROR got 1 type parameter, but receiver base type declares 2 */ [_]) m1() {}
func (T[_, _]) m2() {}
// TODO(gri) this error is unfortunate (issue #51343)
func (T /* ERROR got 3 arguments but 2 type parameters */ [_, _, _]) m3() {}

View file

@ -290,15 +290,14 @@ func (check *Checker) typesSummary(list []Type, variadic bool) string {
return "(" + strings.Join(res, ", ") + ")"
}
func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
measure := func(x int, unit string) string {
s := fmt.Sprintf("%d %s", x, unit)
if x != 1 {
s += "s"
}
return s
func measure(x int, unit string) string {
if x != 1 {
unit += "s"
}
return fmt.Sprintf("%d %s", x, unit)
}
func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
vars := measure(nvars, "variable")
vals := measure(nvals, "value")
rhs0 := rhs[0]

View file

@ -112,7 +112,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
// - the receiver specification acts as local declaration for its type parameters, which may be blank
_, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true)
if len(rparams) > 0 {
sig.rparams = bindTParams(check.declareTypeParams(nil, rparams))
tparams := check.declareTypeParams(nil, rparams)
sig.rparams = bindTParams(tparams)
// Blank identifiers don't get declared, so naive type-checking of the
// receiver type expression would fail in Checker.collectParams below,
// when Checker.ident cannot resolve the _ to a type.
@ -122,11 +123,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
// lookup in the scope.
for i, p := range rparams {
if p.Name == "_" {
tpar := sig.rparams.At(i)
if check.recvTParamMap == nil {
check.recvTParamMap = make(map[*ast.Ident]*TypeParam)
}
check.recvTParamMap[p] = tpar
check.recvTParamMap[p] = tparams[i]
}
}
// determine receiver type to get its type parameters
@ -142,22 +142,23 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
}
}
// provide type parameter bounds
// - only do this if we have the right number (otherwise an error is reported elsewhere)
if sig.RecvTypeParams().Len() == len(recvTParams) {
// We have a list of *TypeNames but we need a list of Types.
list := make([]Type, sig.RecvTypeParams().Len())
for i, t := range sig.RecvTypeParams().list() {
list[i] = t
check.mono.recordCanon(t, recvTParams[i])
}
smap := makeSubstMap(recvTParams, list)
for i, tpar := range sig.RecvTypeParams().list() {
bound := recvTParams[i].bound
// bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the
// current context.
tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil)
if len(tparams) == len(recvTParams) {
smap := makeRenameMap(recvTParams, tparams)
for i, tpar := range tparams {
recvTPar := recvTParams[i]
check.mono.recordCanon(tpar, recvTPar)
// recvTPar.bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the current
// context.
tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil)
}
} else if len(tparams) < len(recvTParams) {
// Reporting an error here is a stop-gap measure to avoid crashes in the
// compiler when a type parameter/argument cannot be inferred later. It
// may lead to follow-on errors (see issues #51339, #51343).
// TODO(gri) find a better solution
got := measure(len(tparams), "type parameter")
check.errorf(recvPar, _BadRecv, "got %s, but receiver base type declares %d", got, len(recvTParams))
}
}
}

View file

@ -0,0 +1,16 @@
// 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.
// This file is tested when running "go test -run Manual"
// without source arguments. Use for one-off debugging.
package p
type T[P any, B *P] struct{}
func (T /* ERROR cannot use generic type */ ) m0() {}
func (/* ERROR got 1 type parameter, but receiver base type declares 2 */ T[_]) m1() {}
func (T[_, _]) m2() {}
// TODO(gri) this error is unfortunate (issue #51343)
func (T /* ERROR got 3 arguments but 2 type parameters */ [_, _, _]) m3() {}