Permalink
Cannot retrieve contributors at this time
// Copyright 2012 The Go Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
// This file implements commonly used type predicates. | |
package types | |
import ( | |
"go/token" | |
) | |
// isNamed reports whether typ has a name. | |
// isNamed may be called with types that are not fully set up. | |
func isNamed(typ Type) bool { | |
switch typ.(type) { | |
case *Basic, *Named, *_TypeParam, *instance: | |
return true | |
} | |
return false | |
} | |
// isGeneric reports whether a type is a generic, uninstantiated type (generic | |
// signatures are not included). | |
func isGeneric(typ Type) bool { | |
// A parameterized type is only instantiated if it doesn't have an instantiation already. | |
named, _ := typ.(*Named) | |
return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil | |
} | |
func is(typ Type, what BasicInfo) bool { | |
switch t := optype(typ).(type) { | |
case *Basic: | |
return t.info&what != 0 | |
case *_Sum: | |
return t.is(func(typ Type) bool { return is(typ, what) }) | |
} | |
return false | |
} | |
func isBoolean(typ Type) bool { return is(typ, IsBoolean) } | |
func isInteger(typ Type) bool { return is(typ, IsInteger) } | |
func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) } | |
func isFloat(typ Type) bool { return is(typ, IsFloat) } | |
func isComplex(typ Type) bool { return is(typ, IsComplex) } | |
func isNumeric(typ Type) bool { return is(typ, IsNumeric) } | |
func isString(typ Type) bool { return is(typ, IsString) } | |
// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not | |
// produce the expected result because a type list that contains both an integer | |
// and a floating-point type is neither (all) integers, nor (all) floats. | |
// Use isIntegerOrFloat instead. | |
func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) } | |
// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ). | |
func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) } | |
// isTyped reports whether typ is typed; i.e., not an untyped | |
// constant or boolean. isTyped may be called with types that | |
// are not fully set up. | |
func isTyped(typ Type) bool { | |
// isTyped is called with types that are not fully | |
// set up. Must not call asBasic()! | |
// A *Named or *instance type is always typed, so | |
// we only need to check if we have a true *Basic | |
// type. | |
t, _ := typ.(*Basic) | |
return t == nil || t.info&IsUntyped == 0 | |
} | |
// isUntyped(typ) is the same as !isTyped(typ). | |
func isUntyped(typ Type) bool { | |
return !isTyped(typ) | |
} | |
func isOrdered(typ Type) bool { return is(typ, IsOrdered) } | |
func isConstType(typ Type) bool { | |
// Type parameters are never const types. | |
t, _ := under(typ).(*Basic) | |
return t != nil && t.info&IsConstType != 0 | |
} | |
// IsInterface reports whether typ is an interface type. | |
func IsInterface(typ Type) bool { | |
return asInterface(typ) != nil | |
} | |
// Comparable reports whether values of type T are comparable. | |
func Comparable(T Type) bool { | |
return comparable(T, nil) | |
} | |
func comparable(T Type, seen map[Type]bool) bool { | |
if seen[T] { | |
return true | |
} | |
if seen == nil { | |
seen = make(map[Type]bool) | |
} | |
seen[T] = true | |
// If T is a type parameter not constrained by any type | |
// list (i.e., it's underlying type is the top type), | |
// T is comparable if it has the == method. Otherwise, | |
// the underlying type "wins". For instance | |
// | |
// interface{ comparable; type []byte } | |
// | |
// is not comparable because []byte is not comparable. | |
if t := asTypeParam(T); t != nil && optype(t) == theTop { | |
return t.Bound()._IsComparable() | |
} | |
switch t := optype(T).(type) { | |
case *Basic: | |
// assume invalid types to be comparable | |
// to avoid follow-up errors | |
return t.kind != UntypedNil | |
case *Pointer, *Interface, *Chan: | |
return true | |
case *Struct: | |
for _, f := range t.fields { | |
if !comparable(f.typ, seen) { | |
return false | |
} | |
} | |
return true | |
case *Array: | |
return comparable(t.elem, seen) | |
case *_Sum: | |
pred := func(t Type) bool { | |
return comparable(t, seen) | |
} | |
return t.is(pred) | |
case *_TypeParam: | |
return t.Bound()._IsComparable() | |
} | |
return false | |
} | |
// hasNil reports whether a type includes the nil value. | |
func hasNil(typ Type) bool { | |
switch t := optype(typ).(type) { | |
case *Basic: | |
return t.kind == UnsafePointer | |
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: | |
return true | |
case *_Sum: | |
return t.is(hasNil) | |
} | |
return false | |
} | |
// identical reports whether x and y are identical types. | |
// Receivers of Signature types are ignored. | |
func (check *Checker) identical(x, y Type) bool { | |
return check.identical0(x, y, true, nil) | |
} | |
// identicalIgnoreTags reports whether x and y are identical types if tags are ignored. | |
// Receivers of Signature types are ignored. | |
func (check *Checker) identicalIgnoreTags(x, y Type) bool { | |
return check.identical0(x, y, false, nil) | |
} | |
// An ifacePair is a node in a stack of interface type pairs compared for identity. | |
type ifacePair struct { | |
x, y *Interface | |
prev *ifacePair | |
} | |
func (p *ifacePair) identical(q *ifacePair) bool { | |
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x | |
} | |
// For changes to this code the corresponding changes should be made to unifier.nify. | |
func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { | |
// types must be expanded for comparison | |
x = expandf(x) | |
y = expandf(y) | |
if x == y { | |
return true | |
} | |
switch x := x.(type) { | |
case *Basic: | |
// Basic types are singletons except for the rune and byte | |
// aliases, thus we cannot solely rely on the x == y check | |
// above. See also comment in TypeName.IsAlias. | |
if y, ok := y.(*Basic); ok { | |
return x.kind == y.kind | |
} | |
case *Array: | |
// Two array types are identical if they have identical element types | |
// and the same array length. | |
if y, ok := y.(*Array); ok { | |
// If one or both array lengths are unknown (< 0) due to some error, | |
// assume they are the same to avoid spurious follow-on errors. | |
return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p) | |
} | |
case *Slice: | |
// Two slice types are identical if they have identical element types. | |
if y, ok := y.(*Slice); ok { | |
return check.identical0(x.elem, y.elem, cmpTags, p) | |
} | |
case *Struct: | |
// Two struct types are identical if they have the same sequence of fields, | |
// and if corresponding fields have the same names, and identical types, | |
// and identical tags. Two embedded fields are considered to have the same | |
// name. Lower-case field names from different packages are always different. | |
if y, ok := y.(*Struct); ok { | |
if x.NumFields() == y.NumFields() { | |
for i, f := range x.fields { | |
g := y.fields[i] | |
if f.embedded != g.embedded || | |
cmpTags && x.Tag(i) != y.Tag(i) || | |
!f.sameId(g.pkg, g.name) || | |
!check.identical0(f.typ, g.typ, cmpTags, p) { | |
return false | |
} | |
} | |
return true | |
} | |
} | |
case *Pointer: | |
// Two pointer types are identical if they have identical base types. | |
if y, ok := y.(*Pointer); ok { | |
return check.identical0(x.base, y.base, cmpTags, p) | |
} | |
case *Tuple: | |
// Two tuples types are identical if they have the same number of elements | |
// and corresponding elements have identical types. | |
if y, ok := y.(*Tuple); ok { | |
if x.Len() == y.Len() { | |
if x != nil { | |
for i, v := range x.vars { | |
w := y.vars[i] | |
if !check.identical0(v.typ, w.typ, cmpTags, p) { | |
return false | |
} | |
} | |
} | |
return true | |
} | |
} | |
case *Signature: | |
// Two function types are identical if they have the same number of parameters | |
// and result values, corresponding parameter and result types are identical, | |
// and either both functions are variadic or neither is. Parameter and result | |
// names are not required to match. | |
// Generic functions must also have matching type parameter lists, but for the | |
// parameter names. | |
if y, ok := y.(*Signature); ok { | |
return x.variadic == y.variadic && | |
check.identicalTParams(x.tparams, y.tparams, cmpTags, p) && | |
check.identical0(x.params, y.params, cmpTags, p) && | |
check.identical0(x.results, y.results, cmpTags, p) | |
} | |
case *_Sum: | |
// Two sum types are identical if they contain the same types. | |
// (Sum types always consist of at least two types. Also, the | |
// the set (list) of types in a sum type consists of unique | |
// types - each type appears exactly once. Thus, two sum types | |
// must contain the same number of types to have chance of | |
// being equal. | |
if y, ok := y.(*_Sum); ok && len(x.types) == len(y.types) { | |
// Every type in x.types must be in y.types. | |
// Quadratic algorithm, but probably good enough for now. | |
// TODO(gri) we need a fast quick type ID/hash for all types. | |
L: | |
for _, x := range x.types { | |
for _, y := range y.types { | |
if Identical(x, y) { | |
continue L // x is in y.types | |
} | |
} | |
return false // x is not in y.types | |
} | |
return true | |
} | |
case *Interface: | |
// Two interface types are identical if they have the same set of methods with | |
// the same names and identical function types. Lower-case method names from | |
// different packages are always different. The order of the methods is irrelevant. | |
if y, ok := y.(*Interface); ok { | |
// If identical0 is called (indirectly) via an external API entry point | |
// (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in | |
// that case, interfaces are expected to be complete and lazy completion | |
// here is not needed. | |
if check != nil { | |
check.completeInterface(token.NoPos, x) | |
check.completeInterface(token.NoPos, y) | |
} | |
a := x.allMethods | |
b := y.allMethods | |
if len(a) == len(b) { | |
// Interface types are the only types where cycles can occur | |
// that are not "terminated" via named types; and such cycles | |
// can only be created via method parameter types that are | |
// anonymous interfaces (directly or indirectly) embedding | |
// the current interface. Example: | |
// | |
// type T interface { | |
// m() interface{T} | |
// } | |
// | |
// If two such (differently named) interfaces are compared, | |
// endless recursion occurs if the cycle is not detected. | |
// | |
// If x and y were compared before, they must be equal | |
// (if they were not, the recursion would have stopped); | |
// search the ifacePair stack for the same pair. | |
// | |
// This is a quadratic algorithm, but in practice these stacks | |
// are extremely short (bounded by the nesting depth of interface | |
// type declarations that recur via parameter types, an extremely | |
// rare occurrence). An alternative implementation might use a | |
// "visited" map, but that is probably less efficient overall. | |
q := &ifacePair{x, y, p} | |
for p != nil { | |
if p.identical(q) { | |
return true // same pair was compared before | |
} | |
p = p.prev | |
} | |
if debug { | |
assertSortedMethods(a) | |
assertSortedMethods(b) | |
} | |
for i, f := range a { | |
g := b[i] | |
if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) { | |
return false | |
} | |
} | |
return true | |
} | |
} | |
case *Map: | |
// Two map types are identical if they have identical key and value types. | |
if y, ok := y.(*Map); ok { | |
return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p) | |
} | |
case *Chan: | |
// Two channel types are identical if they have identical value types | |
// and the same direction. | |
if y, ok := y.(*Chan); ok { | |
return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p) | |
} | |
case *Named: | |
// Two named types are identical if their type names originate | |
// in the same type declaration. | |
if y, ok := y.(*Named); ok { | |
// TODO(gri) Why is x == y not sufficient? And if it is, | |
// we can just return false here because x == y | |
// is caught in the very beginning of this function. | |
return x.obj == y.obj | |
} | |
case *_TypeParam: | |
// nothing to do (x and y being equal is caught in the very beginning of this function) | |
// case *instance: | |
// unreachable since types are expanded | |
case *bottom, *top: | |
// Either both types are theBottom, or both are theTop in which | |
// case the initial x == y check will have caught them. Otherwise | |
// they are not identical. | |
case nil: | |
// avoid a crash in case of nil type | |
default: | |
unreachable() | |
} | |
return false | |
} | |
func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool { | |
if len(x) != len(y) { | |
return false | |
} | |
for i, x := range x { | |
y := y[i] | |
if !check.identical0(x.typ.(*_TypeParam).bound, y.typ.(*_TypeParam).bound, cmpTags, p) { | |
return false | |
} | |
} | |
return true | |
} | |
// Default returns the default "typed" type for an "untyped" type; | |
// it returns the incoming type for all other types. The default type | |
// for untyped nil is untyped nil. | |
// | |
func Default(typ Type) Type { | |
if t, ok := typ.(*Basic); ok { | |
switch t.kind { | |
case UntypedBool: | |
return Typ[Bool] | |
case UntypedInt: | |
return Typ[Int] | |
case UntypedRune: | |
return universeRune // use 'rune' name | |
case UntypedFloat: | |
return Typ[Float64] | |
case UntypedComplex: | |
return Typ[Complex128] | |
case UntypedString: | |
return Typ[String] | |
} | |
} | |
return typ | |
} |