From af1245832781cbba2796f35a279473a68100b442 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 23 Dec 2019 15:10:45 -0800 Subject: [PATCH] go/types: always create a bound for each contract parameter Also: - simplify Contract struct - rename some variables for more consistency Change-Id: Icd10b47ac97d11870c6d4590c154b7efe158c134 --- src/go/types/contracts.go | 50 +++++++++++++++++---------------------- src/go/types/decl.go | 9 ++----- src/go/types/object.go | 11 ++++----- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index bfc6732db8..db99113318 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -16,43 +16,37 @@ type contractType struct{} func (contractType) String() string { return "" } func (contractType) Underlying() Type { panic("unreachable") } -func (check *Checker) contractDecl(contr *Contract, e *ast.ContractSpec) { - assert(contr.typ == nil) +func (check *Checker) contractDecl(obj *Contract, cdecl *ast.ContractSpec) { + assert(obj.typ == nil) // contracts don't have types, but we need to set a type to // detect recursive declarations and satisfy various assertions - contr.typ = new(contractType) + obj.typ = new(contractType) - check.openScope(e, "contract") + check.openScope(cdecl, "contract") defer check.closeScope() - tparams := check.declareTypeParams(nil, e.TParams, nil) + tparams := check.declareTypeParams(nil, cdecl.TParams, nil) - // TODO(gri) review this - we probably don't need lazy allocation anymore - // Each type parameter's constraints are represented by a (lazily allocated) named interface. // 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 - // representing the type constraints for each of the type parameters (C1 for P1, C2 for P2, etc.). - bounds := make(map[*TypeName]*Named) - ifaceFor := func(tpar *TypeName) *Named { - named := bounds[tpar] - if named == nil { - index := tpar.typ.(*TypeParam).index - tname := NewTypeName(e.Pos(), check.pkg, contr.name+string(subscript(uint64(index))), nil) - named = NewNamed(tname, new(Interface), nil) - named.tparams = tparams - bounds[tpar] = named - } - return named + // representing the (possibly empty) type constraints for each of the type parameters + // (C1 for P1, C2 for P2, etc.). + bounds := make([]*Named, len(tparams)) + for i, tpar := range tparams { + tname := NewTypeName(tpar.Pos(), check.pkg, obj.name+string(subscript(uint64(i))), nil) + named := NewNamed(tname, new(Interface), nil) + named.tparams = tparams + bounds[i] = named } // collect constraints - for _, c := range e.Constraints { + for _, c := range cdecl.Constraints { if c.Param != nil { // If a type name is present, it must be one of the contract's type parameters. pos := c.Param.Pos() - obj := check.scope.Lookup(c.Param.Name) - if obj == nil { + tobj := check.scope.Lookup(c.Param.Name) + if tobj == nil { check.errorf(pos, "%s not declared by contract", c.Param.Name) continue } @@ -82,8 +76,8 @@ func (check *Checker) contractDecl(contr *Contract, e *ast.ContractSpec) { } } - tpar := obj.(*TypeName) - ifaceName := ifaceFor(tpar) + tpar := tobj.(*TypeName) + ifaceName := bounds[tpar.typ.(*TypeParam).index] iface := ifaceName.underlying.(*Interface) switch nmethods { case 0: @@ -124,7 +118,7 @@ func (check *Checker) contractDecl(contr *Contract, e *ast.ContractSpec) { // ignore and continue } if len(c.Types) != 1 { - check.invalidAST(e.Pos(), "contract contains incorrect (possibly embedded contract) entry") + check.invalidAST(cdecl.Pos(), "contract contains incorrect (possibly embedded contract) entry") continue } // TODO(gri) we can probably get away w/o checking this (even if the AST is broken) @@ -141,11 +135,11 @@ func (check *Checker) contractDecl(contr *Contract, e *ast.ContractSpec) { // complete interfaces for _, bound := range bounds { - check.completeInterface(e.Pos(), bound.underlying.(*Interface)) + check.completeInterface(cdecl.Pos(), bound.underlying.(*Interface)) } - contr.TParams = tparams - contr.Bounds = bounds + obj.TParams = tparams + obj.Bounds = bounds } func (check *Checker) collectTypeConstraints(pos token.Pos, list []Type, types []ast.Expr) []Type { diff --git a/src/go/types/decl.go b/src/go/types/decl.go index a81a85c5c7..4477b27ff1 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -644,13 +644,8 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam targs[i] = tparam.typ } for i, name := range f.Names { - bat := obj.boundsAt(i) - // TODO(gri) eliminate this nil test by ensuring that all - // contract parameters have an associated bound - if bat == nil { - continue - } - setBoundAt(index+i, check.instantiate(name.Pos(), bat, targs, nil)) + bound := obj.boundsAt(i) + setBoundAt(index+i, check.instantiate(name.Pos(), bound, targs, nil)) } goto next } diff --git a/src/go/types/object.go b/src/go/types/object.go index 2880c24123..8028982344 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -323,8 +323,8 @@ func (*Func) isDependency() {} // a function may be a dependency of an initializ // A Contract represents a declared contract. type Contract struct { object - TParams []*TypeName // type parameters in declaration order - Bounds map[*TypeName]*Named // underlying type is always *Interface + TParams []*TypeName // type parameters in declaration order + Bounds []*Named // underlying type is always *Interface } // NewContract returns a new contract. @@ -335,7 +335,7 @@ func NewContract(pos token.Pos, pkg *Package, name string) *Contract { // boundsAt returns the (named) interface for the respective // contract type parameter with the given index. func (c *Contract) boundsAt(index int) *Named { - return c.Bounds[c.TParams[index]] + return c.Bounds[index] } // A Label represents a declared label. @@ -411,12 +411,11 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { WriteType(buf, tpar.typ, qf) } buf.WriteString(") {") - i := 0 - for tpar, bound := range obj.Bounds { + for i, bound := range obj.Bounds { if i > 0 { buf.WriteString("; ") } - WriteType(buf, tpar.typ, qf) + WriteType(buf, obj.TParams[i].typ, qf) buf.WriteByte(' ') WriteType(buf, bound, qf) buf.WriteString(" = ")