mirror of
https://github.com/golang/go
synced 2024-11-14 09:00:21 -07:00
[dev.typeparams] go/types: import predicates.go from dev.go2go
Changes from dev.go2go: + Update some isComparable cases to use the seen map. + Tiny updates to comments. Change-Id: Iafd85d60835f17a87f514d9774cae07c183ee6cc Reviewed-on: https://go-review.googlesource.com/c/go/+/278594 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
f38da2cbb6
commit
09abd23d9e
@ -58,11 +58,16 @@ the type (and constant value, if any) is recorded via Info.Types, if present.
|
||||
|
||||
type opPredicates map[token.Token]func(Type) bool
|
||||
|
||||
var unaryOpPredicates = opPredicates{
|
||||
token.ADD: isNumeric,
|
||||
token.SUB: isNumeric,
|
||||
token.XOR: isInteger,
|
||||
token.NOT: isBoolean,
|
||||
var unaryOpPredicates opPredicates
|
||||
|
||||
func init() {
|
||||
// Setting unaryOpPredicates in init avoids declaration cycles.
|
||||
unaryOpPredicates = opPredicates{
|
||||
token.ADD: isNumeric,
|
||||
token.SUB: isNumeric,
|
||||
token.XOR: isInteger,
|
||||
token.NOT: isBoolean,
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
|
||||
@ -785,20 +790,25 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
||||
x.mode = value
|
||||
}
|
||||
|
||||
var binaryOpPredicates = opPredicates{
|
||||
token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) },
|
||||
token.SUB: isNumeric,
|
||||
token.MUL: isNumeric,
|
||||
token.QUO: isNumeric,
|
||||
token.REM: isInteger,
|
||||
var binaryOpPredicates opPredicates
|
||||
|
||||
token.AND: isInteger,
|
||||
token.OR: isInteger,
|
||||
token.XOR: isInteger,
|
||||
token.AND_NOT: isInteger,
|
||||
func init() {
|
||||
// Setting binaryOpPredicates in init avoids declaration cycles.
|
||||
binaryOpPredicates = opPredicates{
|
||||
token.ADD: isNumericOrString,
|
||||
token.SUB: isNumeric,
|
||||
token.MUL: isNumeric,
|
||||
token.QUO: isNumeric,
|
||||
token.REM: isInteger,
|
||||
|
||||
token.LAND: isBoolean,
|
||||
token.LOR: isBoolean,
|
||||
token.AND: isInteger,
|
||||
token.OR: isInteger,
|
||||
token.XOR: isInteger,
|
||||
token.AND_NOT: isInteger,
|
||||
|
||||
token.LAND: isBoolean,
|
||||
token.LOR: isBoolean,
|
||||
}
|
||||
}
|
||||
|
||||
// The binary expression e may be nil. It's passed in for better error messages only.
|
||||
|
@ -11,73 +11,79 @@ import (
|
||||
"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
|
||||
return false
|
||||
}
|
||||
|
||||
func isBoolean(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsBoolean != 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 isInteger(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsInteger != 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 isUnsigned(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsUnsigned != 0
|
||||
}
|
||||
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) }
|
||||
|
||||
func isFloat(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsFloat != 0
|
||||
}
|
||||
// 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) }
|
||||
|
||||
func isComplex(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsComplex != 0
|
||||
}
|
||||
|
||||
func isNumeric(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsNumeric != 0
|
||||
}
|
||||
|
||||
func isString(typ Type) bool {
|
||||
t, ok := typ.Underlying().(*Basic)
|
||||
return ok && t.info&IsString != 0
|
||||
}
|
||||
// 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.
|
||||
@ -94,7 +100,19 @@ func comparable(T Type, seen map[Type]bool) bool {
|
||||
}
|
||||
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
|
||||
@ -110,17 +128,26 @@ func comparable(T Type, seen map[Type]bool) bool {
|
||||
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
|
||||
}
|
||||
@ -147,7 +174,12 @@ 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
|
||||
}
|
||||
@ -224,12 +256,38 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||
// 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
|
||||
@ -306,10 +364,25 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||
// 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()
|
||||
@ -318,6 +391,19 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user