From 5d80216f876964274e1727711064dec1d4232518 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 13 Jan 2020 11:22:09 -0800 Subject: [PATCH] go/types: ensure a TypeParam always has a non-nil type bound Plus minor cleanups. Change-Id: I2508a576ad56d55fb188aa78c861afd0d032cf80 --- src/go/types/NOTES | 4 +++- src/go/types/builtins.go | 2 +- src/go/types/call.go | 2 +- src/go/types/contracts.go | 2 +- src/go/types/decl.go | 2 +- src/go/types/subst.go | 21 +++++++++------------ src/go/types/type.go | 6 +++++- src/go/types/typexpr.go | 2 +- 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 5b2f77b718..ece9709c44 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -11,6 +11,7 @@ TODO - debug (and error msg) printing of generic instantiated types needs some work - improve error messages! - use []*TypeParam for tparams in subst? (unclear) +- should we use nil instead of &emptyInterface for no type bounds (as an optimization)? ---------------------------------------------------------------------------------------------------- KNOWN ISSUES @@ -87,4 +88,5 @@ DESIGN/IMPLEMENTATION - 1/7/2020: The current implementation permits empty type parameter lists as in: "func f(type)(x int)" but we cannot call such a function as "f()(1)"; the empty type argument list causes problems. - We should either disallow empty type parameter lists or document this well. + Document that we allow empty type parameter list declarations, but not empty actual type parameter + lists. (We could allow them for types, but that doesn't seem consistent and probably is confusing). diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index e2ef8ea09f..c0165c64f6 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -695,7 +695,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { // construct a suitable new type parameter tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "", nil) - ptyp := check.NewTypeParam(tpar, 0, nil) // assigns type to tpar as a side-effect + ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect ptyp.bound = &Interface{types: resTypes, allMethods: markComplete, allTypes: resTypes} return ptyp diff --git a/src/go/types/call.go b/src/go/types/call.go index a3c3bff19b..0a6535161f 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -441,7 +441,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { x.typ = exp.typ x.id = exp.id default: - check.dump("unexpected object %v", exp) + check.dump("%v: unexpected object %v", e.Sel.Pos(), exp) unreachable() } x.expr = e diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index 5b50cc352f..f2cb88f08a 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -17,7 +17,7 @@ func (check *Checker) contractDecl(obj *Contract, cdecl *ast.ContractSpec) { check.openScope(cdecl, "contract") defer check.closeScope() - tparams := check.declareTypeParams(nil, cdecl.TParams, nil) + tparams := check.declareTypeParams(nil, cdecl.TParams, &emptyInterface) // Given a contract C(P1, P2, ... Pn) { ... } we construct named types C1(P1, P2, ... Pn), // C2(P1, P2, ... Pn), ... Cn(P1, P2, ... Pn) with the respective underlying interfaces diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 235779a527..46ae273131 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -644,7 +644,7 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam } if targs != nil { // obj denotes a valid contract that is instantiated with targs - check.errorf(f.Type.Pos(), "contract instantiation not yet implemented") + check.errorf(f.Type.Pos(), "explicit contract instantiation not yet implemented") } else { // obj denotes a valid uninstantiated contract => // use the declared type parameters as "arguments" diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 60f13e3298..2a21811b0d 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -38,7 +38,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist case *Signature: tparams = t.tparams default: - check.dump(">>> trying to instantiate %s", typ) + check.dump("%v: cannot instantiate %v", pos, typ) unreachable() // only defined types and (defined) functions can be generic } @@ -50,29 +50,26 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist return Typ[Invalid] } - // TODO(gri) should we check for len(tparams) == 0? + if len(tparams) == 0 { + return typ // nothing to do (minor optimization) + } // check bounds for i, tname := range tparams { - targ := targs[i] - tpar := tname.typ.(*TypeParam) - // TODO(gri) decide if we want to keep this or standardize on the empty interface - // (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 type bound (optimization) + iface := tpar.Interface() + if iface.Empty() { + continue // no type bound } + targ := targs[i] + // best position for error reporting pos := pos if i < len(poslist) { pos = poslist[i] } - // determine type parameter bound - iface := tpar.Interface() - // The type parameter bound is parameterized with the same type parameters // as the instantiated type; before we can use it for bounds checking we // need to instantiate it with the type arguments with which we instantiate diff --git a/src/go/types/type.go b/src/go/types/type.go index 7536ab716b..8cad041acd 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -359,7 +359,10 @@ func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMe // Empty reports whether t is the empty interface. // The interface must have been completed. -func (t *Interface) Empty() bool { t.assertCompleteness(); return len(t.allMethods) == 0 } +func (t *Interface) Empty() bool { + t.assertCompleteness() + return len(t.allMethods) == 0 && len(t.allTypes) == 0 +} // includes reports whether the interface t includes the type typ. func (t *Interface) includes(typ Type) bool { @@ -537,6 +540,7 @@ type TypeParam struct { // NewTypeParam returns a new TypeParam. func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam { + assert(bound != nil) typ := &TypeParam{check.nextId, obj, index, bound} check.nextId++ if obj.typ == nil { diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 6bdcb7dd3c..985ac06c06 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -179,7 +179,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // (TODO(gri) this is not working because the code doesn't allow an uninstantiated parameterized recv type) _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true) if len(rparams) > 0 { - sig.rparams = check.declareTypeParams(nil, rparams, nil) + sig.rparams = check.declareTypeParams(nil, rparams, &emptyInterface) // determine receiver type to get its type parameters // and the respective type parameter bounds var recvTParams []*TypeName