mirror of
https://github.com/golang/go
synced 2024-11-14 18:40:29 -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
|
type opPredicates map[token.Token]func(Type) bool
|
||||||
|
|
||||||
var unaryOpPredicates = opPredicates{
|
var unaryOpPredicates opPredicates
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Setting unaryOpPredicates in init avoids declaration cycles.
|
||||||
|
unaryOpPredicates = opPredicates{
|
||||||
token.ADD: isNumeric,
|
token.ADD: isNumeric,
|
||||||
token.SUB: isNumeric,
|
token.SUB: isNumeric,
|
||||||
token.XOR: isInteger,
|
token.XOR: isInteger,
|
||||||
token.NOT: isBoolean,
|
token.NOT: isBoolean,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
|
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
|
||||||
@ -785,8 +790,12 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
|||||||
x.mode = value
|
x.mode = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var binaryOpPredicates = opPredicates{
|
var binaryOpPredicates opPredicates
|
||||||
token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) },
|
|
||||||
|
func init() {
|
||||||
|
// Setting binaryOpPredicates in init avoids declaration cycles.
|
||||||
|
binaryOpPredicates = opPredicates{
|
||||||
|
token.ADD: isNumericOrString,
|
||||||
token.SUB: isNumeric,
|
token.SUB: isNumeric,
|
||||||
token.MUL: isNumeric,
|
token.MUL: isNumeric,
|
||||||
token.QUO: isNumeric,
|
token.QUO: isNumeric,
|
||||||
@ -799,6 +808,7 @@ var binaryOpPredicates = opPredicates{
|
|||||||
|
|
||||||
token.LAND: isBoolean,
|
token.LAND: isBoolean,
|
||||||
token.LOR: isBoolean,
|
token.LOR: isBoolean,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The binary expression e may be nil. It's passed in for better error messages only.
|
// The binary expression e may be nil. It's passed in for better error messages only.
|
||||||
|
@ -11,73 +11,79 @@ import (
|
|||||||
"sort"
|
"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 {
|
func isNamed(typ Type) bool {
|
||||||
if _, ok := typ.(*Basic); ok {
|
switch typ.(type) {
|
||||||
return ok
|
case *Basic, *Named, *TypeParam, *instance:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
_, ok := typ.(*Named)
|
return false
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBoolean(typ Type) bool {
|
// isGeneric reports whether a type is a generic, uninstantiated type (generic
|
||||||
t, ok := typ.Underlying().(*Basic)
|
// signatures are not included).
|
||||||
return ok && t.info&IsBoolean != 0
|
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 {
|
func is(typ Type, what BasicInfo) bool {
|
||||||
t, ok := typ.Underlying().(*Basic)
|
switch t := optype(typ).(type) {
|
||||||
return ok && t.info&IsInteger != 0
|
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 {
|
func isBoolean(typ Type) bool { return is(typ, IsBoolean) }
|
||||||
t, ok := typ.Underlying().(*Basic)
|
func isInteger(typ Type) bool { return is(typ, IsInteger) }
|
||||||
return ok && t.info&IsUnsigned != 0
|
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 {
|
// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not
|
||||||
t, ok := typ.Underlying().(*Basic)
|
// produce the expected result because a type list that contains both an integer
|
||||||
return ok && t.info&IsFloat != 0
|
// 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 {
|
// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ).
|
||||||
t, ok := typ.Underlying().(*Basic)
|
func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) }
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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 {
|
func isTyped(typ Type) bool {
|
||||||
t, ok := typ.Underlying().(*Basic)
|
// isTyped is called with types that are not fully
|
||||||
return !ok || t.info&IsUntyped == 0
|
// 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 {
|
func isUntyped(typ Type) bool {
|
||||||
t, ok := typ.Underlying().(*Basic)
|
return !isTyped(typ)
|
||||||
return ok && t.info&IsUntyped != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOrdered(typ Type) bool {
|
func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
|
||||||
t, ok := typ.Underlying().(*Basic)
|
|
||||||
return ok && t.info&IsOrdered != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func isConstType(typ Type) bool {
|
func isConstType(typ Type) bool {
|
||||||
t, ok := typ.Underlying().(*Basic)
|
t := asBasic(typ)
|
||||||
return ok && t.info&IsConstType != 0
|
return t != nil && t.info&IsConstType != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInterface reports whether typ is an interface type.
|
// IsInterface reports whether typ is an interface type.
|
||||||
func IsInterface(typ Type) bool {
|
func IsInterface(typ Type) bool {
|
||||||
_, ok := typ.Underlying().(*Interface)
|
return asInterface(typ) != nil
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comparable reports whether values of type T are comparable.
|
// 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
|
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:
|
case *Basic:
|
||||||
// assume invalid types to be comparable
|
// assume invalid types to be comparable
|
||||||
// to avoid follow-up errors
|
// to avoid follow-up errors
|
||||||
@ -110,17 +128,26 @@ func comparable(T Type, seen map[Type]bool) bool {
|
|||||||
return true
|
return true
|
||||||
case *Array:
|
case *Array:
|
||||||
return comparable(t.elem, seen)
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasNil reports whether a type includes the nil value.
|
// hasNil reports whether a type includes the nil value.
|
||||||
func hasNil(typ Type) bool {
|
func hasNil(typ Type) bool {
|
||||||
switch t := typ.Underlying().(type) {
|
switch t := optype(typ).(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
return t.kind == UnsafePointer
|
return t.kind == UnsafePointer
|
||||||
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
|
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
|
||||||
return true
|
return true
|
||||||
|
case *Sum:
|
||||||
|
return t.is(hasNil)
|
||||||
}
|
}
|
||||||
return false
|
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
|
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 {
|
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 {
|
if x == y {
|
||||||
return true
|
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 result values, corresponding parameter and result types are identical,
|
||||||
// and either both functions are variadic or neither is. Parameter and result
|
// and either both functions are variadic or neither is. Parameter and result
|
||||||
// names are not required to match.
|
// 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 {
|
if y, ok := y.(*Signature); ok {
|
||||||
return x.variadic == y.variadic &&
|
return x.variadic == y.variadic &&
|
||||||
|
check.identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
|
||||||
check.identical0(x.params, y.params, cmpTags, p) &&
|
check.identical0(x.params, y.params, cmpTags, p) &&
|
||||||
check.identical0(x.results, y.results, 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:
|
case *Interface:
|
||||||
// Two interface types are identical if they have the same set of methods with
|
// 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
|
// 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
|
// Two named types are identical if their type names originate
|
||||||
// in the same type declaration.
|
// in the same type declaration.
|
||||||
if y, ok := y.(*Named); ok {
|
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
|
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:
|
case nil:
|
||||||
|
// avoid a crash in case of nil type
|
||||||
|
|
||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
@ -318,6 +391,19 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
|||||||
return false
|
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;
|
// Default returns the default "typed" type for an "untyped" type;
|
||||||
// it returns the incoming type for all other types. The default type
|
// it returns the incoming type for all other types. The default type
|
||||||
// for untyped nil is untyped nil.
|
// for untyped nil is untyped nil.
|
||||||
|
Loading…
Reference in New Issue
Block a user