"sort"
)
+// isNamed reports whether typ has a name.
+// isNamed may be called with types that are not fully set up.
func isNamed(typ Type) bool {
- if _, ok := typ.(*Basic); ok {
- return ok
+ switch typ.(type) {
+ case *Basic, *Named, *TypeParam, *instance:
+ return true
}
- _, ok := typ.(*Named)
- return ok
-}
-
-func isBoolean(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsBoolean != 0
-}
-
-func isInteger(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsInteger != 0
-}
-
-func isUnsigned(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsUnsigned != 0
-}
-
-func isFloat(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsFloat != 0
-}
-
-func isComplex(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsComplex != 0
+ return false
}
-func isNumeric(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsNumeric != 0
+// 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 isString(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsString != 0
+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 {
- t, ok := typ.Underlying().(*Basic)
- return !ok || t.info&IsUntyped == 0
+ // 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 {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsUntyped != 0
+ return !isTyped(typ)
}
-func isOrdered(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsOrdered != 0
-}
+func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
func isConstType(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.info&IsConstType != 0
+ t := asBasic(typ)
+ return t != nil && t.info&IsConstType != 0
}
// IsInterface reports whether typ is an interface type.
func IsInterface(typ Type) bool {
- _, ok := typ.Underlying().(*Interface)
- return ok
+ return asInterface(typ) != nil
}
// Comparable reports whether values of type T are comparable.
}
seen[T] = true
- switch t := T.Underlying().(type) {
+ // 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 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 := typ.Underlying().(type) {
+ 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
}
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
}
// 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
// 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.