go/types: set receiver type bounds for methods

Enable a few more tests that now work correctly.

Change-Id: I7efe91660c2896d4d8279b86831aa7de2ae7c0ad
This commit is contained in:
Robert Griesemer 2019-12-11 16:04:34 -08:00
parent d49c915592
commit 2aeeba6836
5 changed files with 61 additions and 14 deletions

View File

@ -730,15 +730,41 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
fdecl := decl.fdecl
if fdecl.IsMethod() {
_, _, tparams := check.unpackRecv(fdecl.Recv.List[0].Type, true)
_, rname, tparams := check.unpackRecv(fdecl.Recv.List[0].Type, true)
if len(tparams) > 0 {
// TODO(gri) need to provide contract
// (check that number of parameters match is done when type-checking the receiver expression)
// declare the method's receiver type parameters
check.openScope(fdecl, "receiver type parameters")
defer check.closeScope()
// TODO(gri) can we use (an adjusted version of) collectTypeParams here?
for i, name := range tparams {
obj.tparams = append(obj.tparams, check.declareTypeParam(name, i, nil))
// collect and declare the type parameters
var list []Type // list of corresponding *TypeParams
for index, name := range tparams {
tpar := check.declareTypeParam(name, index, nil)
obj.tparams = append(obj.tparams, tpar)
list = append(list, tpar.typ)
}
// determine receiver type to get its type parameters
// and the respective type parameter bounds
var recvTParams []*TypeName
if rname != nil {
// recv should be a Named type (otherwise an error is reported elsewhere)
if recv, _ := check.genericType(rname).(*Named); recv != nil {
recvTParams = recv.obj.tparams
}
}
// provide type parameter bounds
// - only do this if we have the right number (otherwise an error is reported elsewhere)
if len(list) == len(recvTParams) {
assert(len(list) == len(obj.tparams))
for index, tname := range obj.tparams {
bound := recvTParams[index].typ.(*TypeParam).bound
// bound is (possibly) parameterized in the context of the
// receiver type declaration. Substitute parameters for the
// current context.
if bound != nil {
bound = check.subst(tname.pos, bound, recvTParams, list)
tname.typ.(*TypeParam).bound = bound
}
}
}
}
} else if fdecl.TParams != nil {

View File

@ -43,17 +43,17 @@ contract G(Node, Edge) {
type Graph (type Node, Edge G) struct { /* ... */ }
func New (type Node, Edge G) (nodes []Node) *Graph(Node, Edge) { panic("unimplemented") }
func New (type Node, Edge G) (nodes []Node) *Graph(Node, Edge)
// func (g *Graph(N, E)) ShortestPath(from, to N) []E { panic("unimplemented") }
func (g *Graph(N, E)) ShortestPath(from, to N) []E
// Same Graph using interface bounds instead of a contract.
type AltGraph (type Node NodeFace(Edge), Edge EdgeFace(Node)) struct { }
func AltNew (type Node NodeFace(Edge), Edge EdgeFace(Node)) (nodes []Node) *AltGraph(Node, Edge) { panic("unimplemented") }
func AltNew (type Node NodeFace(Edge), Edge EdgeFace(Node)) (nodes []Node) *AltGraph(Node, Edge)
// func (g *AltGraph(N, E)) ShortestPath(from, to N) []E { panic("unimplemented") }
func (g *AltGraph(N, E)) ShortestPath(from, to N) []E
type NodeFace(type Edge) interface {
Edges() []Edge

View File

@ -501,7 +501,7 @@ func (check *Checker) collectObjects() {
// rtyp is a pointer receiver, rname is the receiver type name, and tparams are its
// type parameters, if any. The type parameters are only unpacked if unpackParams is
// set. If rname is nil, the receiver is unusable (i.e., the source has a bug which we
// cannot easily work aound with).
// cannot easily work around).
func (check *Checker) unpackRecv(rtyp ast.Expr, unpackParams bool) (ptr bool, rname *ast.Ident, tparams []*ast.Ident) {
L: // unpack receiver type
for {

View File

@ -76,6 +76,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
// determine type parameter bound
iface := tpar.Interface()
// TODO(gri) document/explain why the substitution below is correct
//check.dump(">>> %s: iface before: %s", pos, iface)
bound := check.subst(pos, tpar.bound, tparams, targs).Underlying()
switch b := bound.(type) {
@ -102,6 +103,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
if targ, _ := targ.Underlying().(*TypeParam); targ != nil {
for _, t := range targ.Interface().types {
if !includesType(t, iface) {
// TODO(gri) match this error message with the one below (or vice versa)
check.softErrorf(pos, "%s does not satisfy %s (missing type %s)", targ, tpar.bound, t)
break
}
@ -123,6 +125,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
}
// includesType reports whether iface includes typ
// TODO(gri) make this a method of *Interface
func includesType(typ Type, iface *Interface) bool {
for _, t := range iface.types {
if Identical(typ.Underlying(), t) {
@ -139,13 +142,28 @@ func (check *Checker) subst(pos token.Pos, typ Type, tpars []*TypeName, targs []
if len(tpars) == 0 {
return typ
}
subst := subster{pos, check, make(map[Type]Type), tpars, targs}
// common cases
switch t := typ.(type) {
case *Basic:
return typ // nothing to do
case *TypeParam:
for i, tpar := range tpars {
if tpar.typ == t {
return targs[i]
}
}
return typ
}
// general case
subst := subster{check, pos, make(map[Type]Type), tpars, targs}
return subst.typ(typ)
}
type subster struct {
pos token.Pos
check *Checker
pos token.Pos
cache map[Type]Type
tpars []*TypeName
targs []Type
@ -303,6 +321,9 @@ func (subst *subster) typ(typ Type) Type {
}
}
case *Contract:
panic("unimplemented")
default:
panic("unimplemented")
}

View File

@ -137,7 +137,7 @@ func (check *Checker) definedType(e ast.Expr, def *Named) Type {
return typ
}
// generic us like typ bit the type must be an (uninstantiated) generic type.
// generic is like typ but the type must be an (uninstantiated) generic type.
func (check *Checker) genericType(e ast.Expr) Type {
typ := check.typInternal(e, nil)
assert(isTyped(typ))