From 3c6943daac72df90efa08585b812643c8e68d05a Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 12 Aug 2019 14:13:31 -0700 Subject: [PATCH] go/types: more work on checking contract satisfaction Change-Id: Ibe54e448603c685d714310ff83d4193150d47ebd --- src/go/types/call.go | 2 +- src/go/types/contracts.go | 18 +++++++++++++++--- src/go/types/testdata/contracts.go2 | 3 +-- src/go/types/testdata/tmp.go2 | 21 +++++++++++++++++---- src/go/types/type.go | 24 ++++++++++++++++++------ 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/go/types/call.go b/src/go/types/call.go index 44b0ddd3f2..d8a95f31f7 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -522,7 +522,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // _before_ calling NewMethodSet: LookupFieldOrMethod completes // any incomplete interfaces so they are available to NewMethodSet // (which assumes that interfaces have been completed already). - typ := x.typ + typ := unpack(x.typ) if x.mode == variable { // If typ is not an (unnamed) pointer or an interface, // use *typ instead, because the method set of *typ diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index 8d254c3563..11e8806d83 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -259,19 +259,31 @@ func (check *Checker) satisfyContract(contr *Contract, targs []Type) bool { for i, targ := range targs { iface := contr.ifaceAt(i) - if iface == nil { - continue // no constraints - } // If iface is parameterized, we need to replace the type parameters // with the respective type arguments. if isParameterized(iface) { panic("unimplemented") } + // use interface type of type parameter, if any + // TODO(gri) is this the correct place for this? (why not in missinMethod?) + targ = unpack(targ) // targ must implement iface if m, _ := check.missingMethod(targ, iface, true); m != nil { + // check.dump("missing %s (%s, %s)", m, targ, iface) return false } } return true } + +// unpack returns the interface type of a type parameter, +// otherwise it just returns the argument type. +// TODO(gri) This function is currently uses if a few places. +// Need to determine if there's a better way to handle this. +func unpack(typ Type) Type { + if tpar, _ := typ.(*TypeParam); tpar != nil { + return tpar.Interface() + } + return typ +} diff --git a/src/go/types/testdata/contracts.go2 b/src/go/types/testdata/contracts.go2 index 5cd0420cce..1b6c1c93fc 100644 --- a/src/go/types/testdata/contracts.go2 +++ b/src/go/types/testdata/contracts.go2 @@ -92,8 +92,7 @@ contract Stringer(T) { type List(type T Stringer) struct{ data T - // TODO(gri) cannot handle this yet - //link *List(T) + link *List(T) } var _ List(MyData) diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 3a3e4dfe33..75a2c34c2c 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -1,10 +1,23 @@ package p -// Type instantiation must satisfy the contract. -type Stringer contract(T) { +contract Stringer(T) { T String() string } -type List(type T Stringer) struct{} +type List(type T Stringer) struct{ + data T + link *List(T) +} -var _ List /* ERROR not satisfied */ (int) \ No newline at end of file +type MyData string + +func (s MyData) String() string { return string(s) } + +var _ List(MyData) + +func _(type T Stringer)(s []T) (r []string) { + for _, x := range s { + r = append(r, x.String()) + } + return +} \ No newline at end of file diff --git a/src/go/types/type.go b/src/go/types/type.go index 4fee5cc0d8..bf27599d1b 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -371,7 +371,8 @@ func (t *Interface) Complete() *Interface { case other == nil: methods = append(methods, m) case explicit: - panic("duplicate method " + m.name) + // TODO(gri) enable again once contracts code calls Complete with correct interfaces + // panic("duplicate method " + m.name) default: // check method signatures after all locally embedded interfaces are computed todo = append(todo, m, other.(*Func)) @@ -515,12 +516,16 @@ type Contract struct { // ifaceAt returns the interface matching for the respective // contract type parameter with the given index. If c is nil -// the result is nil. +// the result is the empty interface. func (c *Contract) ifaceAt(index int) *Interface { + var iface *Interface if c != nil { - return c.IFaces[c.TParams[index]] + iface = c.IFaces[c.TParams[index]] } - return nil + if iface == nil { + iface = &emptyInterface + } + return iface } // A TypeParam represents a type parameter type. @@ -539,6 +544,13 @@ func NewTypeParam(obj *TypeName, index int, contr *Contract) *TypeParam { return typ } +// Interface returns the type parameter's interface as +// specified via its contract. If there is no contract, +// the result is the empty interface. +func (t *TypeParam) Interface() *Interface { + return t.contr.ifaceAt(t.index) +} + // Implementations for Type methods. func (b *Basic) Underlying() Type { return b } @@ -554,7 +566,7 @@ func (c *Chan) Underlying() Type { return c } func (t *Named) Underlying() Type { return t.underlying } func (p *Parameterized) Underlying() Type { return p.tname.typ.Underlying() } func (c *Contract) Underlying() Type { return c } -func (c *TypeParam) Underlying() Type { return c } +func (t *TypeParam) Underlying() Type { return t } func (b *Basic) String() string { return TypeString(b, nil) } func (a *Array) String() string { return TypeString(a, nil) } @@ -569,4 +581,4 @@ func (c *Chan) String() string { return TypeString(c, nil) } func (t *Named) String() string { return TypeString(t, nil) } func (p *Parameterized) String() string { return TypeString(p, nil) } func (c *Contract) String() string { return TypeString(c, nil) } -func (c *TypeParam) String() string { return TypeString(c, nil) } +func (t *TypeParam) String() string { return TypeString(t, nil) }