mirror of https://github.com/golang/go.git
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:
parent
1f06612063
commit
5891f8c720
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue