go/types: substitute type parameters when checking type param. bounds

Change-Id: Ib97d1b3928629bc073b32fde48709369213a4bbb
This commit is contained in:
Robert Griesemer 2019-12-11 12:46:03 -08:00
parent d941f3db1d
commit a30fb05aa2
4 changed files with 27 additions and 7 deletions

View File

@ -5,8 +5,8 @@ TODO
- interface embedding doesn't take care of literal type constraints yet
(need an allTypes list, like we have an allMethods list?)
OPEN ISSUES
- contracts slip through in places where only types are permitted
DESIGN DECISIONS

View File

@ -615,6 +615,7 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
index = 0
for _, f := range list.List {
var bound Type
var isContract bool
if f.Type != nil {
typ := check.typ(f.Type)
if typ != Typ[Invalid] {
@ -628,6 +629,7 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
break // cannot use this contract
}
bound = typ
isContract = true
default:
check.errorf(f.Type.Pos(), "%s is not an interface or contract", typ)
}
@ -636,10 +638,16 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
// set the type parameter's bound
// (do this even if bound == nil to make sure index is correctly increasing)
for _, name := range f.Names {
for i, name := range f.Names {
tname := tparams[index]
assert(name.Name == tname.name) // keep - this assertion caught index errors
tname.typ.(*TypeParam).bound = bound
assert(name.Name == tname.name) // catch index errors
// TODO(gri) enable the then branch to force interfaces for all
// bounds and unpack the interface in the else branch
if false && isContract {
tname.typ.(*TypeParam).bound = bound.Underlying().(*Contract).ifaceAt(i)
} else {
tname.typ.(*TypeParam).bound = bound
}
index++
}
}

View File

@ -51,7 +51,7 @@ func New (type Node, Edge G) (nodes []Node) *Graph(Node, Edge) { panic("unimplem
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) { panic("unimplemented") }
type NodeFace(type Edge) interface {
Edges() []Edge

View File

@ -50,6 +50,8 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
return Typ[Invalid]
}
// TODO(gri) should we check for len(tparams) == 0?
// check bounds
for i, tname := range tparams {
targ := targs[i]
@ -62,7 +64,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
// (keeping it may make sense because it's a common scenario and it may
// be more efficient to check)
if tpar.bound == nil {
continue // no bound (optimization)
continue // no type bound (optimization)
}
// best position for error reporting
@ -74,6 +76,16 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
// determine type parameter bound
iface := tpar.Interface()
//check.dump(">>> %s: iface before: %s", pos, iface)
bound := check.subst(pos, tpar.bound, tparams, targs).Underlying()
switch b := bound.(type) {
case *Interface:
iface = b
case *Contract:
iface = b.ifaceAt(i)
}
//check.dump(">>> %s: iface after : %s", pos, iface)
// targ must implement iface (methods)
if m, _ := check.missingMethod(targ, iface, true); m != nil {
check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m)
@ -124,7 +136,7 @@ func includesType(typ Type, iface *Interface) bool {
// subst returns the type typ with its type parameters tparams replaced by
// the corresponding type arguments targs, recursively.
func (check *Checker) subst(pos token.Pos, typ Type, tpars []*TypeName, targs []Type) Type {
assert(len(tpars) == len(targs)) // bounds may have only some type parameters
assert(len(tpars) == len(targs))
if len(tpars) == 0 {
return typ
}