diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index 466daed482..d26e1b3388 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -29,13 +29,13 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { // each type parameter's constraints are represented by an interface ifaces := make(map[*TypeName]*Interface) - addMethod := func(tpar *TypeName, m *Func) { + ifaceFor := func(tpar *TypeName) *Interface { iface := ifaces[tpar] if iface == nil { iface = new(Interface) ifaces[tpar] = iface } - iface.methods = append(iface.methods, m) + return iface } // collect constraints @@ -75,6 +75,7 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { } tpar := obj.(*TypeName) + iface := ifaceFor(tpar) switch nmethods { case 0: // type constraints @@ -92,7 +93,9 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { check.errorf(texpr.Pos(), "invalid type constraint %s (%s)", typ, why) continue } - // TODO(gri) add type + // add type + iface.types = append(iface.types, typ) + } case 1: @@ -115,7 +118,7 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { // add the method mname := c.MNames[0] m := NewFunc(mname.Pos(), check.pkg, mname.Name, sig) - addMethod(tpar, m) + iface.methods = append(iface.methods, m) default: // ignore (error was reported earlier) @@ -243,6 +246,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. + // TODO(gri) fix this if IsParameterized(iface) { panic("unimplemented") } @@ -252,6 +256,24 @@ func (check *Checker) satisfyContract(contr *Contract, targs []Type) bool { // check.dump("missing %s (%s, %s)", m, targ, iface) return false } + // targ's underlying type must also be one of the interface types listed, if any + if len(iface.types) > 0 { + utyp := targ.Underlying() + // TODO(gri) Cannot handle a type argument that is itself parameterized for now + switch utyp.(type) { + case *Interface, *Contract: + panic("unimplemented") + } + ok := false + for _, t := range iface.types { + // if we find one matching type, we're ok + if Identical(utyp, t) { + ok = true + break + } + } + return ok + } } return true diff --git a/src/go/types/testdata/contracts.go2 b/src/go/types/testdata/contracts.go2 index cd7df220a4..8265fbd0e3 100644 --- a/src/go/types/testdata/contracts.go2 +++ b/src/go/types/testdata/contracts.go2 @@ -101,3 +101,18 @@ var _ List /* ERROR not satisfied */ (int) type MyData string func (s MyData) String() string { return string(s) } + +// Contracts that mention types can only be satisfied by similar types. + +contract C3(T) { + T int16, int32, int, struct{x int} +} + +type T2 (type _ C3) struct{} + +var _ T2 /* ERROR not satisfied */ (int8) +var _ T2(int16) +var _ T2(int32) +var _ T2 /* ERROR not satisfied */ (int64) +var _ T2(int) +var _ T2(struct{x int}) diff --git a/src/go/types/type.go b/src/go/types/type.go index 1a2838a173..13ab290a27 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -249,6 +249,9 @@ type Interface struct { embeddeds []Type // ordered list of explicitly embedded types allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) + + // for contracts + types []Type // for contracts } // emptyInterface represents the empty (completed) interface