mirror of
https://github.com/golang/go
synced 2024-11-02 09:03:03 +00:00
[dev.typeparams] go/types: delay interface check for type bounds
This is a port of CL 331690 to go/types. It diverges from that CL due to the different representation of Fields in the AST. Change-Id: I3ae9ac3a0172dc58ac748f28772d87b00db0732a Reviewed-on: https://go-review.googlesource.com/c/go/+/335034 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
5f50a6442e
commit
0f4198b5e2
3 changed files with 65 additions and 36 deletions
|
@ -724,13 +724,8 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
|
||||
}
|
||||
|
||||
func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeName) {
|
||||
// Type parameter lists should not be empty. The parser will
|
||||
// complain but we still may get an incorrect AST: ignore it.
|
||||
if list.NumFields() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
func (check *Checker) collectTypeParams(list *ast.FieldList) []*TypeName {
|
||||
var tparams []*TypeName
|
||||
// Declare type parameters up-front, with empty interface as type bound.
|
||||
// The scope of type parameters starts at the beginning of the type parameter
|
||||
// list (so we can have mutually recursive parameterized interfaces).
|
||||
|
@ -738,46 +733,22 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
|
|||
tparams = check.declareTypeParams(tparams, f.Names)
|
||||
}
|
||||
|
||||
setBoundAt := func(at int, bound Type) {
|
||||
assert(IsInterface(bound))
|
||||
tparams[at].typ.(*TypeParam).bound = bound
|
||||
}
|
||||
|
||||
index := 0
|
||||
var bound Type
|
||||
for _, f := range list.List {
|
||||
if f.Type == nil {
|
||||
goto next
|
||||
}
|
||||
|
||||
// The predeclared identifier "any" is visible only as a constraint
|
||||
// in a type parameter list. Look for it before general constraint
|
||||
// resolution.
|
||||
if tident, _ := unparen(f.Type).(*ast.Ident); tident != nil && tident.Name == "any" && check.lookup("any") == nil {
|
||||
bound = universeAny
|
||||
} else {
|
||||
bound = check.typ(f.Type)
|
||||
}
|
||||
|
||||
// type bound must be an interface
|
||||
// TODO(gri) We should delay the interface check because
|
||||
// we may not have a complete interface yet:
|
||||
// type C(type T C) interface {}
|
||||
// (issue #39724).
|
||||
if _, ok := under(bound).(*Interface); ok {
|
||||
// Otherwise, set the bound for each type parameter.
|
||||
bound = check.boundType(f.Type)
|
||||
for i := range f.Names {
|
||||
setBoundAt(index+i, bound)
|
||||
}
|
||||
} else if bound != Typ[Invalid] {
|
||||
check.errorf(f.Type, _Todo, "%s is not an interface", bound)
|
||||
tparams[index+i].typ.(*TypeParam).bound = bound
|
||||
}
|
||||
|
||||
next:
|
||||
index += len(f.Names)
|
||||
}
|
||||
|
||||
return
|
||||
return tparams
|
||||
}
|
||||
|
||||
func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident) []*TypeName {
|
||||
|
@ -795,6 +766,23 @@ func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident)
|
|||
return tparams
|
||||
}
|
||||
|
||||
// boundType type-checks the type expression e and returns its type, or Typ[Invalid].
|
||||
// The type must be an interface, including the predeclared type "any".
|
||||
func (check *Checker) boundType(e ast.Expr) Type {
|
||||
// The predeclared identifier "any" is visible only as a type bound in a type parameter list.
|
||||
if name, _ := unparen(e).(*ast.Ident); name != nil && name.Name == "any" && check.lookup("any") == nil {
|
||||
return universeAny
|
||||
}
|
||||
|
||||
bound := check.typ(e)
|
||||
check.later(func() {
|
||||
if _, ok := under(bound).(*Interface); !ok && bound != Typ[Invalid] {
|
||||
check.errorf(e, _Todo, "%s is not an interface", bound)
|
||||
}
|
||||
})
|
||||
return bound
|
||||
}
|
||||
|
||||
func (check *Checker) collectMethods(obj *TypeName) {
|
||||
// get associated methods
|
||||
// (Checker.collectObjects only collects methods with non-blank names;
|
||||
|
|
37
src/go/types/testdata/fixedbugs/issue40789.go2
vendored
Normal file
37
src/go/types/testdata/fixedbugs/issue40789.go2
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2021 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 main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 6,
|
||||
"b": 7,
|
||||
}
|
||||
fmt.Println(copyMap[map[string]int, string, int](m))
|
||||
}
|
||||
|
||||
type Map[K comparable, V any] interface {
|
||||
map[K] V
|
||||
}
|
||||
|
||||
func copyMap[M Map[K, V], K comparable, V any](m M) M {
|
||||
m1 := make(M)
|
||||
for k, v := range m {
|
||||
m1[k] = v
|
||||
}
|
||||
return m1
|
||||
}
|
||||
|
||||
// simpler test case from the same issue
|
||||
|
||||
type A[X comparable] interface {
|
||||
[]X
|
||||
}
|
||||
|
||||
func f[B A[X], X comparable]() B {
|
||||
return nil
|
||||
}
|
|
@ -623,7 +623,11 @@ func (check *Checker) newTypeParam(obj *TypeName, index int, bound Type) *TypePa
|
|||
}
|
||||
|
||||
func (t *TypeParam) Bound() *Interface {
|
||||
iface := asInterface(t.bound)
|
||||
// we may not have an interface (error reported elsewhere)
|
||||
iface, _ := under(t.bound).(*Interface)
|
||||
if iface == nil {
|
||||
return &emptyInterface
|
||||
}
|
||||
// use the type bound position if we have one
|
||||
pos := token.NoPos
|
||||
if n, _ := t.bound.(*Named); n != nil {
|
||||
|
|
Loading…
Reference in a new issue