1
0
mirror of https://github.com/golang/go synced 2024-11-26 07:17:59 -07:00

[dev.typeparams] cmd/compile/internal/types2: use converter functions rather than methods

This change replaces methods with functions to reduce the API surface of
types2.Type and to match the approach taken in go/types. The converter
methods for Named and TypeParam will be addressed in a follow-up CL.

Also: Fixed behavior of optype to return the underlying type for
      arguments that are not type parameters.

Change-Id: Ia369c796754bc33bbaf0c9c8478badecb729279b
Reviewed-on: https://go-review.googlesource.com/c/go/+/293291
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-02-17 16:04:59 -08:00
parent 5ecb9a7887
commit 5e4da8670b
15 changed files with 108 additions and 155 deletions

View File

@ -129,16 +129,5 @@ func (t anyType) Under() types2.Type { return t }
func (t anyType) String() string { return "any" }
// types2.aType is not exported for now so we need to implemented these here.
func (anyType) Basic() *types2.Basic { return nil }
func (anyType) Array() *types2.Array { return nil }
func (anyType) Slice() *types2.Slice { return nil }
func (anyType) Struct() *types2.Struct { return nil }
func (anyType) Pointer() *types2.Pointer { return nil }
func (anyType) Tuple() *types2.Tuple { return nil }
func (anyType) Signature() *types2.Signature { return nil }
func (anyType) Sum() *types2.Sum { return nil }
func (anyType) Interface() *types2.Interface { return nil }
func (anyType) Map() *types2.Map { return nil }
func (anyType) Chan() *types2.Chan { return nil }
func (anyType) Named() *types2.Named { return nil }
func (anyType) TypeParam() *types2.TypeParam { return nil }

View File

@ -52,7 +52,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
// x.typ is typed
// A generic (non-instantiated) function value cannot be assigned to a variable.
if sig := x.typ.Signature(); sig != nil && len(sig.tparams) > 0 {
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context)
}

View File

@ -82,7 +82,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// of S and the respective parameter passing rules apply."
S := x.typ
var T Type
if s := S.Slice(); s != nil {
if s := asSlice(S); s != nil {
T = s.elem
} else {
check.invalidArgf(x, "%s is not a slice", x)
@ -210,7 +210,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Close:
// close(c)
c := x.typ.Chan()
c := asChan(x.typ)
if c == nil {
check.invalidArgf(x, "%s is not a channel", x)
return
@ -286,7 +286,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// the argument types must be of floating-point type
f := func(x Type) Type {
if t := x.Basic(); t != nil {
if t := asBasic(x); t != nil {
switch t.kind {
case Float32:
return Typ[Complex64]
@ -320,7 +320,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Copy:
// copy(x, y []T) int
var dst Type
if t := x.typ.Slice(); t != nil {
if t := asSlice(x.typ); t != nil {
dst = t.elem
}
@ -357,7 +357,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Delete:
// delete(m, k)
m := x.typ.Map()
m := asMap(x.typ)
if m == nil {
check.invalidArgf(x, "%s is not a map", x)
return
@ -404,7 +404,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// the argument must be of complex type
f := func(x Type) Type {
if t := x.Basic(); t != nil {
if t := asBasic(x); t != nil {
switch t.kind {
case Complex64:
return Typ[Float32]
@ -757,7 +757,7 @@ func makeSig(res Type, args ...Type) *Signature {
//
func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
if a := p.base.Array(); a != nil {
if a := asArray(p.base); a != nil {
return a
}
}

View File

@ -121,7 +121,7 @@ func (check *Checker) call(x *operand, call *syntax.CallExpr) exprKind {
case 1:
check.expr(x, call.ArgList[0])
if x.mode != invalid {
if t := T.Interface(); t != nil {
if t := asInterface(T); t != nil {
check.completeInterface(nopos, t)
if t.IsConstraint() {
check.errorf(call, "cannot use interface %s in conversion (contains type list or is comparable)", T)
@ -157,7 +157,7 @@ func (check *Checker) call(x *operand, call *syntax.CallExpr) exprKind {
// function/method call
cgocall := x.mode == cgofunc
sig := x.typ.Signature()
sig := asSignature(x.typ)
if sig == nil {
check.invalidOpf(x, "cannot call non-function %s", x)
x.mode = invalid

View File

@ -21,7 +21,7 @@ func (check *Checker) conversion(x *operand, T Type) {
switch {
case constArg && isConstType(T):
// constant conversion
switch t := T.Basic(); {
switch t := asBasic(T); {
case representableConst(x.val, check, t, &x.val):
ok = true
case isInteger(x.typ) && isString(t):
@ -140,26 +140,26 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
}
func isUintptr(typ Type) bool {
t := typ.Basic()
t := asBasic(typ)
return t != nil && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
// TODO(gri): Is this typ.Basic() instead of typ.(*Basic) correct?
// TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct?
// (The former calls typ.Under(), while the latter doesn't.)
// The spec does not say so, but gc claims it is. See also
// issue 6326.
t := typ.Basic()
t := asBasic(typ)
return t != nil && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
return typ.Pointer() != nil
return asPointer(typ) != nil
}
func isBytesOrRunes(typ Type) bool {
if s := typ.Slice(); s != nil {
t := s.elem.Basic()
if s := asSlice(typ); s != nil {
t := asBasic(s.elem)
return t != nil && (t.kind == Byte || t.kind == Rune)
}
return false

View File

@ -110,7 +110,7 @@ func (check *Checker) overflow(x *operand) {
// Typed constants must be representable in
// their type after each constant operation.
if isTyped(x.typ) {
check.representable(x, x.typ.Basic())
check.representable(x, asBasic(x.typ))
return
}
@ -173,7 +173,7 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
return
case syntax.Recv:
typ := x.typ.Chan()
typ := asChan(x.typ)
if typ == nil {
check.invalidOpf(x, "cannot receive from non-channel %s", x)
x.mode = invalid
@ -543,7 +543,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) {
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
old.typ = typ.Basic()
old.typ = asBasic(typ)
check.untyped[x] = old
return
}
@ -1396,7 +1396,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val)
if utyp.key.Interface() != nil {
if asInterface(utyp.key) != nil {
for _, vtyp := range visited[xkey] {
if check.identical(vtyp, x.typ) {
duplicate = true
@ -1465,7 +1465,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
}
if x.mode == value {
if sig := x.typ.Signature(); sig != nil && len(sig.tparams) > 0 {
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
// function instantiation
check.funcInst(x, e)
return expression
@ -1498,7 +1498,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
x.typ = typ.elem
case *Pointer:
if typ := typ.base.Array(); typ != nil {
if typ := asArray(typ.base); typ != nil {
valid = true
length = typ.len
x.mode = variable
@ -1536,7 +1536,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
case *Array:
e = t.elem
case *Pointer:
if t := t.base.Array(); t != nil {
if t := asArray(t.base); t != nil {
e = t.elem
}
case *Slice:
@ -1665,7 +1665,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
x.typ = &Slice{elem: typ.elem}
case *Pointer:
if typ := typ.base.Array(); typ != nil {
if typ := asArray(typ.base); typ != nil {
valid = true
length = typ.len
x.typ = &Slice{elem: typ.elem}
@ -1797,7 +1797,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
case typexpr:
x.typ = &Pointer{base: x.typ}
default:
if typ := x.typ.Pointer(); typ != nil {
if typ := asPointer(x.typ); typ != nil {
x.mode = variable
x.typ = typ.base
} else {

View File

@ -391,7 +391,7 @@ func TestIssue28005(t *testing.T) {
if obj == nil {
t.Fatal("object X not found")
}
iface := obj.Type().Interface() // object X must be an interface
iface := obj.Type().Underlying().(*Interface) // object X must be an interface
if iface == nil {
t.Fatalf("%s is not an interface", obj)
}
@ -414,7 +414,7 @@ func TestIssue28282(t *testing.T) {
it := NewInterfaceType(nil, []Type{et})
it.Complete()
// verify that after completing the interface, the embedded method remains unchanged
want := et.Interface().Method(0)
want := et.Underlying().(*Interface).Method(0)
got := it.Method(0)
if got != want {
t.Fatalf("%s.Method(0): got %q (%p); want %q (%p)", it, got, got, want, want)

View File

@ -314,7 +314,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return
}
if ityp := V.Interface(); ityp != nil {
if ityp := asInterface(V); ityp != nil {
check.completeInterface(nopos, ityp)
// TODO(gri) allMethods is sorted - can do this more efficiently
for _, m := range T.allMethods {
@ -434,7 +434,7 @@ func (check *Checker) assertableTo(V *Interface, T Type, strict bool) (method, w
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."
if T.Interface() != nil && !(strict || forceStrict) {
if asInterface(T) != nil && !(strict || forceStrict) {
return
}
return check.missingMethod(T, V, false)
@ -452,8 +452,8 @@ func deref(typ Type) (Type, bool) {
// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
// (named or unnamed) struct and returns its base. Otherwise it returns typ.
func derefStructPtr(typ Type) Type {
if p := typ.Pointer(); p != nil {
if p.base.Struct() != nil {
if p := asPointer(typ); p != nil {
if asStruct(p.base) != nil {
return p.base
}
}

View File

@ -79,7 +79,7 @@ func isConstType(typ Type) bool {
// IsInterface reports whether typ is an interface type.
func IsInterface(typ Type) bool {
return typ.Interface() != nil
return asInterface(typ) != nil
}
// Comparable reports whether values of type T are comparable.

View File

@ -241,7 +241,7 @@ func (conf *Config) offsetsof(T *Struct) []int64 {
func (conf *Config) offsetof(typ Type, index []int) int64 {
var o int64
for _, i := range index {
s := typ.Struct()
s := asStruct(typ)
o += conf.offsetsof(s)[i]
typ = s.fields[i].typ
}

View File

@ -241,7 +241,7 @@ func typecheck(t *testing.T, path string, filenames []string) {
// Perform checks of API invariants.
// All Objects have a package, except predeclared ones.
errorError := Universe.Lookup("error").Type().Interface().ExplicitMethod(0) // (error).Error
errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error
for id, obj := range info.Uses {
predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
if predeclared == (obj.Pkg() != nil) {

View File

@ -351,7 +351,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
return
}
tch := ch.typ.Chan()
tch := asChan(ch.typ)
if tch == nil {
check.invalidOpf(s, "cannot send to non-chan type %s", ch.typ)
return
@ -890,7 +890,7 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
case *Slice:
return Typ[Int], typ.elem, ""
case *Pointer:
if typ := typ.base.Array(); typ != nil {
if typ := asArray(typ.base); typ != nil {
return Typ[Int], typ.elem, ""
}
case *Map:

View File

@ -27,24 +27,6 @@ type Type interface {
// String returns a string representation of a type.
String() string
// Converters
// A converter must only be called when a type is
// known to be fully set up. A converter returns
// a type's operational type (see comment for optype)
// or nil if the type is receiver is not of the
// respective type.
Basic() *Basic
Array() *Array
Slice() *Slice
Struct() *Struct
Pointer() *Pointer
Tuple() *Tuple
Signature() *Signature
Sum() *Sum
Interface() *Interface
Map() *Map
Chan() *Chan
// If the receiver for Named and TypeParam is of
// the respective type (possibly after unpacking
// an instance type), these methods return that
@ -61,21 +43,6 @@ func (aType) Underlying() Type { panic("unreachable") }
func (aType) Under() Type { panic("unreachable") }
func (aType) String() string { panic("unreachable") }
// Each type is implementing its version of these methods
// (Basic must implement Basic, etc.), the other methods
// are inherited.
func (aType) Basic() *Basic { return nil }
func (aType) Array() *Array { return nil }
func (aType) Slice() *Slice { return nil }
func (aType) Struct() *Struct { return nil }
func (aType) Pointer() *Pointer { return nil }
func (aType) Tuple() *Tuple { return nil }
func (aType) Signature() *Signature { return nil }
func (aType) Sum() *Sum { return nil }
func (aType) Interface() *Interface { return nil }
func (aType) Map() *Map { return nil }
func (aType) Chan() *Chan { return nil }
func (aType) Named() *Named { return nil }
func (aType) TypeParam() *TypeParam { return nil }
@ -256,18 +223,6 @@ func NewTuple(x ...*Var) *Tuple {
// but add all because missing one leads to very confusing bugs.
// TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
// it's too subtle and causes problems.
func (*Tuple) Basic() *Basic { return nil }
func (*Tuple) Array() *Array { return nil }
func (*Tuple) Slice() *Slice { return nil }
func (*Tuple) Struct() *Struct { return nil }
func (*Tuple) Pointer() *Pointer { return nil }
// func (*Tuple) Tuple() *Tuple // implemented below
func (*Tuple) Signature() *Signature { return nil }
func (*Tuple) Sum() *Sum { return nil }
func (*Tuple) Interface() *Interface { return nil }
func (*Tuple) Map() *Map { return nil }
func (*Tuple) Chan() *Chan { return nil }
func (*Tuple) Named() *Named { return nil }
func (*Tuple) TypeParam() *TypeParam { return nil }
@ -408,7 +363,7 @@ func unpack(typ Type) []Type {
if typ == nil {
return nil
}
if sum := typ.Sum(); sum != nil {
if sum := asSum(typ); sum != nil {
return sum.types
}
return []Type{typ}
@ -594,7 +549,7 @@ func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) b
}
for _, e := range t.embeddeds {
// e should be an interface but be careful (it may be invalid)
if e := e.Interface(); e != nil {
if e := asInterface(e); e != nil {
// Cyclic interfaces such as "type E interface { E }" are not permitted
// but they are still constructed and we need to detect such cycles.
if seen[e] {
@ -661,7 +616,7 @@ func (t *Interface) Complete() *Interface {
for _, typ := range t.embeddeds {
utyp := typ.Under()
etyp := utyp.Interface()
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
panic(fmt.Sprintf("%s is not an interface", typ))
@ -775,18 +730,6 @@ func (check *Checker) NewNamed(obj *TypeName, underlying Type, methods []*Func)
// Obj returns the type name for the named type t.
func (t *Named) Obj() *TypeName { return t.obj }
// Converter methods
func (t *Named) Basic() *Basic { return t.Under().Basic() }
func (t *Named) Array() *Array { return t.Under().Array() }
func (t *Named) Slice() *Slice { return t.Under().Slice() }
func (t *Named) Struct() *Struct { return t.Under().Struct() }
func (t *Named) Pointer() *Pointer { return t.Under().Pointer() }
func (t *Named) Tuple() *Tuple { return t.Under().Tuple() }
func (t *Named) Signature() *Signature { return t.Under().Signature() }
func (t *Named) Interface() *Interface { return t.Under().Interface() }
func (t *Named) Map() *Map { return t.Under().Map() }
func (t *Named) Chan() *Chan { return t.Under().Chan() }
// func (t *Named) Named() *Named // declared below
func (t *Named) TypeParam() *TypeParam { return t.Under().TypeParam() }
@ -853,7 +796,7 @@ func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypePa
}
func (t *TypeParam) Bound() *Interface {
iface := t.bound.Interface()
iface := asInterface(t.bound)
// use the type bound position if we have one
pos := nopos
if n, _ := t.bound.(*Named); n != nil {
@ -884,22 +827,9 @@ func optype(typ Type) Type {
}
return theTop
}
return typ
return typ.Under()
}
// Converter methods
func (t *TypeParam) Basic() *Basic { return optype(t).Basic() }
func (t *TypeParam) Array() *Array { return optype(t).Array() }
func (t *TypeParam) Slice() *Slice { return optype(t).Slice() }
func (t *TypeParam) Struct() *Struct { return optype(t).Struct() }
func (t *TypeParam) Pointer() *Pointer { return optype(t).Pointer() }
func (t *TypeParam) Tuple() *Tuple { return optype(t).Tuple() }
func (t *TypeParam) Signature() *Signature { return optype(t).Signature() }
func (t *TypeParam) Sum() *Sum { return optype(t).Sum() }
func (t *TypeParam) Interface() *Interface { return optype(t).Interface() }
func (t *TypeParam) Map() *Map { return optype(t).Map() }
func (t *TypeParam) Chan() *Chan { return optype(t).Chan() }
// func (t *TypeParam) Named() *Named // Named does not unpack type parameters
// func (t *TypeParam) TypeParam() *TypeParam // declared below
@ -917,19 +847,6 @@ type instance struct {
aType
}
// Converter methods
func (t *instance) Basic() *Basic { return t.Under().Basic() }
func (t *instance) Array() *Array { return t.Under().Array() }
func (t *instance) Slice() *Slice { return t.Under().Slice() }
func (t *instance) Struct() *Struct { return t.Under().Struct() }
func (t *instance) Pointer() *Pointer { return t.Under().Pointer() }
func (t *instance) Tuple() *Tuple { return t.Under().Tuple() }
func (t *instance) Signature() *Signature { return t.Under().Signature() }
func (t *instance) Sum() *Sum { return t.Under().Sum() }
func (t *instance) Interface() *Interface { return t.Under().Interface() }
func (t *instance) Map() *Map { return t.Under().Map() }
func (t *instance) Chan() *Chan { return t.Under().Chan() }
func (t *instance) Named() *Named { return t.expand().Named() }
func (t *instance) TypeParam() *TypeParam { return t.expand().TypeParam() }
@ -991,19 +908,6 @@ type top struct {
// theTop is the singleton top type.
var theTop = &top{}
// Type-specific implementations of type converters.
func (t *Basic) Basic() *Basic { return t }
func (t *Array) Array() *Array { return t }
func (t *Slice) Slice() *Slice { return t }
func (t *Struct) Struct() *Struct { return t }
func (t *Pointer) Pointer() *Pointer { return t }
func (t *Tuple) Tuple() *Tuple { return t }
func (t *Signature) Signature() *Signature { return t }
func (t *Sum) Sum() *Sum { return t }
func (t *Interface) Interface() *Interface { return t }
func (t *Map) Map() *Map { return t }
func (t *Chan) Chan() *Chan { return t }
func (t *Named) Named() *Named { return t }
func (t *TypeParam) TypeParam() *TypeParam { return t }
@ -1061,3 +965,63 @@ func (t *TypeParam) String() string { return TypeString(t, nil) }
func (t *instance) String() string { return TypeString(t, nil) }
func (t *bottom) String() string { return TypeString(t, nil) }
func (t *top) String() string { return TypeString(t, nil) }
// Converters
//
// A converter must only be called when a type is
// known to be fully set up. A converter returns
// a type's operational type (see comment for optype)
// or nil if the type argument is not of the
// respective type.
func asBasic(t Type) *Basic {
op, _ := optype(t).(*Basic)
return op
}
func asArray(t Type) *Array {
op, _ := optype(t).(*Array)
return op
}
func asSlice(t Type) *Slice {
op, _ := optype(t).(*Slice)
return op
}
func asStruct(t Type) *Struct {
op, _ := optype(t).(*Struct)
return op
}
func asPointer(t Type) *Pointer {
op, _ := optype(t).(*Pointer)
return op
}
// asTuple is not needed - not provided
func asSignature(t Type) *Signature {
op, _ := optype(t).(*Signature)
return op
}
func asSum(t Type) *Sum {
op, _ := optype(t).(*Sum)
return op
}
func asInterface(t Type) *Interface {
op, _ := optype(t).(*Interface)
return op
}
func asMap(t Type) *Map {
op, _ := optype(t).(*Map)
return op
}
func asChan(t Type) *Chan {
op, _ := optype(t).(*Chan)
return op
}

View File

@ -327,7 +327,7 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited
var writeBounds bool
for _, p := range list {
// bound(p) should be an interface but be careful (it may be invalid)
b := bound(p).Interface()
b := asInterface(bound(p))
if b != nil && !b.Empty() {
writeBounds = true
break
@ -395,7 +395,7 @@ func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visi
} else {
// special case:
// append(s, "foo"...) leads to signature func([]byte, string...)
if t := typ.Basic(); t == nil || t.kind != String {
if t := asBasic(typ); t == nil || t.kind != String {
panic("internal error: string type expected")
}
writeType(buf, typ, qf, visited)

View File

@ -142,7 +142,7 @@ func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) {
// are in the middle of type-checking parameter declarations that might belong to
// interface methods. Delay this check to the end of type-checking.
check.atEnd(func() {
if t := typ.Interface(); t != nil {
if t := asInterface(typ); t != nil {
check.completeInterface(pos, t) // TODO(gri) is this the correct position?
if t.allTypes != nil {
check.softErrorf(pos, "interface contains type constraints (%s)", t.allTypes)
@ -403,7 +403,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
err = "pointer or interface type"
}
}
} else if T := t.Basic(); T != nil {
} else if T := asBasic(t); T != nil {
err = "basic or unnamed type"
if check.conf.CompilerErrorMessages {
check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
@ -968,7 +968,7 @@ func (check *Checker) completeInterface(pos syntax.Pos, ityp *Interface) {
for i, typ := range ityp.embeddeds {
pos := posList[i] // embedding position
utyp := typ.Under()
etyp := utyp.Interface()
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
var format string
@ -1226,7 +1226,7 @@ func (check *Checker) collectTypeConstraints(pos syntax.Pos, types []syntax.Expr
// Note: This is a quadratic algorithm, but type lists tend to be short.
check.atEnd(func() {
for i, t := range list {
if t := t.Interface(); t != nil {
if t := asInterface(t); t != nil {
check.completeInterface(types[i].Pos(), t)
}
if includes(list[:i], t) {