go.tools/go/types: export Comparable and Assertable, simplify Implements

The "static bool" parameter to Implements was confusing; typically we think about interface implementation and type assertion as separate but related concepts, but here they were merged.

LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/58540044
This commit is contained in:
Gordon Klaus 2014-02-03 11:21:01 -08:00 committed by Robert Griesemer
parent ed1b894ade
commit 3e68d08251
6 changed files with 18 additions and 22 deletions

View File

@ -226,6 +226,12 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
return pkg, err return pkg, err
} }
// Assertable reports whether a value of type V can be asserted to have type T.
func Assertable(V *Interface, T Type) bool {
f, _ := MissingMethod(T, V, false)
return f == nil
}
// AssignableTo reports whether a value of type V is assignable to a variable of type T. // AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool { func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V} x := operand{mode: value, typ: V}
@ -238,19 +244,8 @@ func ConvertibleTo(V, T Type) bool {
return x.convertibleTo(nil, T) // config not needed for non-constant x return x.convertibleTo(nil, T) // config not needed for non-constant x
} }
// Implements reports whether a value of type V implements T, as follows: // Implements reports whether type V implements interface T.
// func Implements(V Type, T *Interface) bool {
// 1) For non-interface types V, or if static is set, V implements T if all f, _ := MissingMethod(V, T, true)
// methods of T are present in V. Informally, this reports whether V is a
// subtype of T.
//
// 2) For interface types V, and if static is not set, V implements T if all
// methods of T which are also present in V have matching types. Informally,
// this indicates whether a type assertion x.(T) where x is of type V would
// be legal (the concrete dynamic type of x may implement T even if V does
// not statically implement it).
//
func Implements(V Type, T *Interface, static bool) bool {
f, _ := MissingMethod(V, T, static)
return f == nil return f == nil
} }

View File

@ -567,7 +567,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
switch op { switch op {
case token.EQL, token.NEQ: case token.EQL, token.NEQ:
// spec: "The equality operators == and != apply to operands that are comparable." // spec: "The equality operators == and != apply to operands that are comparable."
defined = isComparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ) defined = Comparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
case token.LSS, token.LEQ, token.GTR, token.GEQ: case token.LSS, token.LEQ, token.GTR, token.GEQ:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered." // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
defined = isOrdered(x.typ) defined = isOrdered(x.typ)

View File

@ -242,7 +242,7 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
// methods of T are present in V. Otherwise (V is an interface and static // methods of T are present in V. Otherwise (V is an interface and static
// is not set), MissingMethod only checks that methods of T which are also // is not set), MissingMethod only checks that methods of T which are also
// present in V have matching types (e.g., for a type assertion x.(T) where // present in V have matching types (e.g., for a type assertion x.(T) where
// x is of interface type typ). // x is of interface type V).
// //
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
// fast path for common case // fast path for common case

View File

@ -217,7 +217,7 @@ func (x *operand) assignableTo(conf *Config, T Type) bool {
// T is an interface type and x implements T // T is an interface type and x implements T
// (Do this check first as it might succeed early.) // (Do this check first as it might succeed early.)
if Ti, ok := Tu.(*Interface); ok { if Ti, ok := Tu.(*Interface); ok {
if m, _ := MissingMethod(x.typ, Ti, true); m == nil { if Implements(x.typ, Ti) {
return true return true
} }
} }

View File

@ -76,8 +76,9 @@ func isInterface(typ Type) bool {
return ok return ok
} }
func isComparable(typ Type) bool { // Comparable reports whether values of type T are comparable.
switch t := typ.Underlying().(type) { func Comparable(T Type) bool {
switch t := T.Underlying().(type) {
case *Basic: case *Basic:
// assume invalid types to be comparable // assume invalid types to be comparable
// to avoid follow-up errors // to avoid follow-up errors
@ -86,13 +87,13 @@ func isComparable(typ Type) bool {
return true return true
case *Struct: case *Struct:
for _, f := range t.fields { for _, f := range t.fields {
if !isComparable(f.typ) { if !Comparable(f.typ) {
return false return false
} }
} }
return true return true
case *Array: case *Array:
return isComparable(t.elem) return Comparable(t.elem)
} }
return false return false
} }

View File

@ -296,7 +296,7 @@ func (check *checker) typInternal(e ast.Expr, def *Named, path []*TypeName) Type
// Delay this check because it requires fully setup types; // Delay this check because it requires fully setup types;
// it is safe to continue in any case (was issue 6667). // it is safe to continue in any case (was issue 6667).
check.delay(func() { check.delay(func() {
if !isComparable(typ.key) { if !Comparable(typ.key) {
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
} }
}) })