diff --git a/src/go/types/call.go b/src/go/types/call.go index 44b0ddd3f2..41afbfe15d 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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 { diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index c25a098c3d..e5b656ca73 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -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 diff --git a/src/go/types/infer.go b/src/go/types/infer.go index 02d1a61c93..782936f65b 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -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 } } diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 50ac670462..4783b7dd6b 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -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} } diff --git a/src/go/types/testdata/chans.go2 b/src/go/types/testdata/chans.go2 index d6ca4df447..40c858c1b7 100644 --- a/src/go/types/testdata/chans.go2 +++ b/src/go/types/testdata/chans.go2 @@ -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 } diff --git a/src/go/types/testdata/map.go2 b/src/go/types/testdata/map.go2 index 82008c7f52..20bff12b0b 100644 --- a/src/go/types/testdata/map.go2 +++ b/src/go/types/testdata/map.go2 @@ -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 diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index ef76018d36..07eca63bcf 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -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 } diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 3bc3e828a6..0d183daf06 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -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)