From edc96118559b12522a63616907db2fb4a22e013c Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 3 Dec 2019 16:42:32 -0800 Subject: [PATCH] go/types: various minor fixes around interface type bounds - print type bounds when printing function signatures - store (named) tyep bound rather than underlying type - first example using parameterized interface type bound working Change-Id: Ic7110039d1e09838c8f33040e887e6a4f038d75d --- src/go/types/call.go | 11 ++++++++--- src/go/types/decl.go | 4 ++-- src/go/types/object.go | 10 ++++++++-- src/go/types/testdata/contracts.go2 | 10 ++++++---- src/go/types/testdata/tmp.go2 | 11 +++++++++++ src/go/types/type.go | 2 +- src/go/types/typexpr.go | 7 ++++--- 7 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/go/types/call.go b/src/go/types/call.go index 0f12e67d83..c464e37574 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -344,7 +344,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { obj Object index []int indirect bool - madeCopy bool // for debugging purposes only ) sel := e.Sel.Name @@ -486,7 +485,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { copy := *m copy.typ = check.subst(e.Pos(), m.typ, m.tparams, targs) obj = © - madeCopy = true // if we made a copy, the method set verification below can't work } } @@ -534,7 +532,14 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // addressability, should we report the type &(x.typ) instead? check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) - if debug && !madeCopy { + // TODO(gri) The verification pass below is disabled for now because + // method sets don't match method lookup in some cases. + // For instance, if we made a copy above when creating a + // custom method for a parameterized received type, the + // method set method doesn't match (no copy there). There + /// may be other situations. + disabled := true + if !disabled && debug { // Verify that LookupFieldOrMethod and MethodSet.Lookup agree. // TODO(gri) This only works because we call LookupFieldOrMethod // _before_ calling NewMethodSet: LookupFieldOrMethod completes diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 530a66e0fa..3f1b9ab695 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -620,14 +620,14 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam if typ != Typ[Invalid] { switch b := typ.Underlying().(type) { case *Interface: - bound = b + bound = typ case *Contract: if len(f.Names) != len(b.TParams) { // TODO(gri) improve error message check.errorf(f.Type.Pos(), "%d type parameters but contract expects %d", len(f.Names), len(b.TParams)) break // cannot use this contract } - bound = b + bound = typ default: check.errorf(f.Type.Pos(), "%s is not an interface or contract", typ) } diff --git a/src/go/types/object.go b/src/go/types/object.go index e873ecef82..e59e7d5f81 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -397,8 +397,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { writeFuncName(buf, obj, qf) // Func.tparams is used for functions and methods; but for methods // these are the receiver parameters. Don't print them twice. - // TODO(gri) receiver and type parameters should be in the Func - // object, not the signature. That should simplify things throughout. + // TODO(gri) Consider putting receiver and type parameters in the Func + // object, not the signature. That might simplify things. if len(obj.tparams) > 0 && (typ == nil || typ.(*Signature).recv == nil) { buf.WriteString("(type ") for i, tname := range obj.tparams { @@ -406,6 +406,12 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { buf.WriteString(", ") } buf.WriteString(tname.name) + if tname.typ != nil && tname.typ.(*TypeParam).bound != nil { + // TODO(gri) Instead of writing the bound with each type + // parameter, consider bundling them up. + buf.WriteByte(' ') + WriteType(buf, tname.typ.(*TypeParam).bound, nil) + } } buf.WriteByte(')') } diff --git a/src/go/types/testdata/contracts.go2 b/src/go/types/testdata/contracts.go2 index 4973a9ee85..f8063df5c6 100644 --- a/src/go/types/testdata/contracts.go2 +++ b/src/go/types/testdata/contracts.go2 @@ -174,12 +174,14 @@ type Adder(type T) interface { Add(T) T } -func adderSum(type T Adder)(data []T) T { +func adderSum(type T Adder(T))(data []T) T { var s T for _, x := range data { - // TODO(gri) make this work - // s = s.Add(x) - _ = x + s = s.Add(x) } return s } + +// TODO(gri) Report an error if we use parameterized interface type bound +// and we forget to pass the type argument. +func buggySig(type T Adder)(data []T) T diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index ad1656ac25..f6c0c1b170 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -11,3 +11,14 @@ contract C(T) { type Cm(type T C) T func (a Cm /* ERROR not satisfied */ (T)) m() T + +// TODO(gri) make this work +/* +type C interface { + type int +} + +type Cm(type T C) T + +func (a Cm(T)) m() T +*/ \ No newline at end of file diff --git a/src/go/types/type.go b/src/go/types/type.go index 67921b3b7d..86dadb4a8a 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -558,7 +558,7 @@ func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypePa // the result is the empty interface. // TODO(gri) should this be Underlying instead? func (t *TypeParam) Interface() *Interface { - switch b := t.bound.(type) { + switch b := t.bound.Underlying().(type) { case nil: return &emptyInterface case *Interface: diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index e22966fc47..b8497bf5f3 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -283,9 +283,10 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { pos := e.Args[i].Pos() pos = e.Pos() // TODO(gri) remove in favor of more accurate pos on prev. line? bound := tpar.typ.(*TypeParam).bound // interface or contract or nil - switch b := bound.(type) { - case nil: - // nothing to do (no bound) + if bound == nil { + continue // nothing to do + } + switch b := bound.Underlying().(type) { case *Interface: iface := check.subst(token.NoPos, b, tname.tparams, targs).(*Interface) if !check.satisfyBound(pos, tpar, targs[i], iface) {