go/types: add cycle detection to IsParameterized

Also: Recognize method expressions with parameterized receivers
and report an error for now (not implemented yet).

Change-Id: I96405b2b739d8e9fff6e9840347d3e78bfe8b6ec
This commit is contained in:
Robert Griesemer 2019-08-16 16:16:22 -07:00
parent 1f06612063
commit 5891f8c720
8 changed files with 50 additions and 31 deletions

View File

@ -112,7 +112,7 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
// if type inference failed, a parametrized result must be invalidated
// (operands cannot have a parametrized type)
if x.mode == value && len(sig.tparams) > 0 && isParameterized(x.typ) {
if x.mode == value && len(sig.tparams) > 0 && IsParameterized(x.typ) {
x.mode = invalid
}
@ -470,6 +470,11 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
check.objDecl(m, nil)
// TODO(gri) fix this
if IsParameterized(x.typ) {
check.errorf(x.pos(), "method expressions/values/calls with parameterized receiver types not implemented yet")
goto Error
}
}
if x.mode == typexpr {

View File

@ -261,7 +261,7 @@ func (check *Checker) satisfyContract(contr *Contract, targs []Type) bool {
iface := contr.ifaceAt(i)
// If iface is parameterized, we need to replace the type parameters
// with the respective type arguments.
if isParameterized(iface) {
if IsParameterized(iface) {
panic("unimplemented")
}
// use interface type of type parameter, if any

View File

@ -25,7 +25,7 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
var indices []int
for i, arg := range args {
par := params.At(i)
if isParameterized(par.typ) {
if IsParameterized(par.typ) {
if arg.mode == invalid {
// TODO(gri) we might still be able to infer all targs by
// simply ignoring (continue) invalid args
@ -91,33 +91,46 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
return targs
}
// isParameterized reports whether typ contains any type parameters.
// TODO(gri) do we need to handle cycles here?
func isParameterized(typ Type) bool {
// IsParameterized reports whether typ contains any type parameters.
func IsParameterized(typ Type) bool {
return isParameterized(typ, make(map[Type]bool))
}
func isParameterized(typ Type, seen map[Type]bool) (res bool) {
// detect cycles
// TODO(gri) can/should this be a Checker map?
if x, ok := seen[typ]; ok {
return x
}
seen[typ] = false
defer func() {
seen[typ] = res
}()
switch t := typ.(type) {
case nil, *Basic, *Named: // TODO(gri) should nil be handled here?
break
case *Array:
return isParameterized(t.elem)
return isParameterized(t.elem, seen)
case *Slice:
return isParameterized(t.elem)
return isParameterized(t.elem, seen)
case *Struct:
for _, fld := range t.fields {
if isParameterized(fld.typ) {
if isParameterized(fld.typ, seen) {
return true
}
}
case *Pointer:
return isParameterized(t.base)
return isParameterized(t.base, seen)
case *Tuple:
n := t.Len()
for i := 0; i < n; i++ {
if isParameterized(t.At(i).typ) {
if isParameterized(t.At(i).typ, seen) {
return true
}
}
@ -128,26 +141,26 @@ func isParameterized(typ Type) bool {
// have methods where the receiver is a contract type
// parameter, by design.
//assert(t.recv == nil || !isParameterized(t.recv.typ))
return isParameterized(t.params) || isParameterized(t.results)
return isParameterized(t.params, seen) || isParameterized(t.results, seen)
case *Interface:
if t.allMethods == nil {
panic("incomplete method")
}
for _, m := range t.allMethods {
if isParameterized(m.typ) {
if isParameterized(m.typ, seen) {
return true
}
}
case *Map:
return isParameterized(t.key) || isParameterized(t.elem)
return isParameterized(t.key, seen) || isParameterized(t.elem, seen)
case *Chan:
return isParameterized(t.elem)
return isParameterized(t.elem, seen)
case *Parameterized:
return isParameterizedList(t.targs)
return isParameterizedList(t.targs, seen)
case *TypeParam:
return true
@ -159,10 +172,14 @@ func isParameterized(typ Type) bool {
return false
}
// isParameterizedList reports whether any type in list is parameterized.
func isParameterizedList(list []Type) bool {
// IsParameterizedList reports whether any type in list is parameterized.
func IsParameterizedList(list []Type) bool {
return isParameterizedList(list, make(map[Type]bool))
}
func isParameterizedList(list []Type, seen map[Type]bool) bool {
for _, t := range list {
if isParameterized(t) {
if isParameterized(t, seen) {
return true
}
}

View File

@ -123,7 +123,7 @@ func (s *subster) typ(typ Type) (res Type) {
case *Named:
// if not all type parameters are known, create a parameterized type
if isParameterizedList(s.targs) {
if IsParameterizedList(s.targs) {
return &Parameterized{t.obj, s.targs}
}

View File

@ -15,7 +15,7 @@ func Ranger(type T)() (*Sender(T), *Receiver(T)) {
d := make(chan bool)
s := &Sender(T){values: c, done: d}
r := &Receiver(T){values: c, done: d}
runtime.SetFinalizer(r, r.finalize)
runtime.SetFinalizer(r, r /* ERROR not implemented */ .finalize)
return s, r
}

View File

@ -44,7 +44,7 @@ func (m *Map(K, V)) find(key K) **node(K, V) {
// If the key is already present, the value is replaced.
// Returns true if this is a new key, false if already present.
func (m *Map(K, V)) Insert(key K, val V) bool {
pn := m.find(key)
pn := m /* ERROR not implemented */ .find(key)
if *pn != nil {
(*pn).val = val
return false
@ -56,7 +56,7 @@ func (m *Map(K, V)) Insert(key K, val V) bool {
// Find returns the value associated with a key, or zero if not present.
// The found result reports whether the key was found.
func (m *Map(K, V)) Find(key K) (V, bool) {
pn := m.find(key)
pn := m /* ERROR not implemented */ .find(key)
if *pn == nil {
var zero V // see the discussion of zero values, above
return zero, false

View File

@ -16,15 +16,12 @@ func (r Receiver(T)) Values() T {
return r.values
}
func Values (type T) (r Receiver(T)) T {
return r.values
}
func (it Iterator(K)) Next() K {
// x := it.r.Values()
x := Values(Pair(K))(it.r)
//x := it.r.Values()
// it : Iterator(K)
// it.r : Receiver(Pair(K))
// it.r.Values: Pair(K)
//var r Receiver(Pair(K))
var x Pair(K) //= (r.Values)()
return x.key
}

View File

@ -150,7 +150,7 @@ func (check *Checker) instantiatedType(e ast.Expr) Type {
typ := check.typ(e)
// A parameterized type where all type arguments are known
// (i.e., not type parameters themselves) can be instantiated.
if ptyp, _ := typ.(*Parameterized); ptyp != nil && !isParameterized(ptyp) {
if ptyp, _ := typ.(*Parameterized); ptyp != nil && !IsParameterized(ptyp) {
typ = check.inst(ptyp.tname, ptyp.targs)
// TODO(gri) can this ever be nil? comment.
if typ == nil {
@ -276,7 +276,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
typ := new(Parameterized)
def.setUnderlying(typ)
if check.parameterizedType(typ, e) {
if isParameterizedList(typ.targs) {
if IsParameterizedList(typ.targs) {
return typ
}
typ := check.inst(typ.tname, typ.targs)