Skip to content

Commit

Permalink
[dev.go2go] go/types: use actual (not underlying) types of type list …
Browse files Browse the repository at this point in the history
…elements for interface satisfaction

This is a rule change in an attempt to streamline interface satisfaction:
If a (constraint) interface contains a type list, a type argument's type
or underlying type must be included in the type list (and the interface's
methods must be present, of course). Until now, a type argument's under-
lying type had to be one of the underlying types of the types in the type
list.

The effect is subtle: Because an underlying type is never a defined type,
if a type list contains defined types, only those defined types satisfy
the interface. If we want the interface to be more flexible, we can in-
clude non-defined types in its type list. In the common case, type lists
simply contain predeclared types (whose underlying types are themselves),
so this doesn't matter. An observable difference appears only for code
that currently uses defined types in constraint type lists: such code
becomes slightly more restrictive.

The primary benefit of this change is that it opens the door for general
(non-constraint) use of interfaces with type lists: Using the same rules
as for constraint satisfaction, a value may be assigned to a variable of
such an interface if it or its underlying type of the value is included
int the interface's type list. If the type list contains only defined types,
the respective interface acts like a sum type: it can only contain values
of those types (and nil).

This CL only changes the rules for constraints. It does not permit
interfaces with type lists for general (non-constraint) use for now.

Change-Id: I3f25b9d883dd865a74c583efee4be2e22258d493
Reviewed-on: https://go-review.googlesource.com/c/go/+/248265
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
  • Loading branch information
griesemer committed Aug 14, 2020
1 parent 722af87 commit af48c2e
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 28 deletions.
8 changes: 4 additions & 4 deletions src/go/types/subst.go
Expand Up @@ -191,7 +191,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
break
}
for _, t := range unpack(targBound.allTypes) {
if !iface.includes(t.Under()) {
if !iface.isSatisfiedBy(t) {
// TODO(gri) match this error message with the one below (or vice versa)
check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
break
Expand All @@ -200,9 +200,9 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
break
}

// Otherwise, targ's underlying type must also be one of the interface types listed, if any.
if !iface.includes(targ.Under()) {
check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, targ.Under(), iface.allTypes)
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
if !iface.isSatisfiedBy(targ) {
check.softErrorf(pos, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, targ.Under(), iface.allTypes)
break
}
}
Expand Down
29 changes: 10 additions & 19 deletions src/go/types/type.go
Expand Up @@ -612,26 +612,17 @@ func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) b
return false
}

// includes reports whether the interface t includes the type typ
// by checking typ against the _underlying_ type of each if the
// types in its typelist.
// Note: Even though the type list is constructed to only contain
// underlying types, it may also contain type parameters (whose
// underlying types are themselves). After instantiation of the
// interface, those type parameters may be replaced with defined
// types, but we still want the underlying types of those (was bug).
// Alternatively, we could recompute the underlying types once,
// after instantiation.
// TODO(gri) investigate the best approach.
func (t *Interface) includes(typ Type) bool {
if t.allTypes != nil {
for _, t := range unpack(t.allTypes) {
if Identical(t.Under(), typ) {
return true
}
}
// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ.
// If the the type list is empty (absent), typ trivially satisfies the interface.
// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive
// "implements" predicate.
func (t *Interface) isSatisfiedBy(typ Type) bool {
t.Complete()
if t.allTypes == nil {
return true
}
return false
types := unpack(t.allTypes)
return includes(types, typ) || includes(types, typ.Under())
}

// Complete computes the interface's method set. It must be called by users of
Expand Down
10 changes: 5 additions & 5 deletions src/go/types/typexpr.go
Expand Up @@ -963,13 +963,13 @@ func intersect(x, y Type) (r Type) {

xtypes := unpack(x)
ytypes := unpack(y)
// Compute the list rtypes which contains only
// Compute the list rtypes which includes only
// types that are in both xtypes and ytypes.
// Quadratic algorithm, but good enough for now.
// TODO(gri) fix this
var rtypes []Type
for _, x := range xtypes {
if contains(ytypes, x) {
if includes(ytypes, x) {
rtypes = append(rtypes, x)
}
}
Expand Down Expand Up @@ -1161,7 +1161,7 @@ func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []
if t := t.Interface(); t != nil {
check.completeInterface(types[i].Pos(), t)
}
if contains(uniques, t) {
if includes(uniques, t) {
check.softErrorf(types[i].Pos(), "duplicate type %s in type list", t)
}
uniques = append(uniques, t)
Expand All @@ -1171,8 +1171,8 @@ func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []
return list
}

// contains reports whether typ is in list
func contains(list []Type, typ Type) bool {
// includes reports whether typ is in list.
func includes(list []Type, typ Type) bool {
for _, e := range list {
if Identical(typ, e) {
return true
Expand Down

0 comments on commit af48c2e

Please sign in to comment.