1
0
mirror of https://github.com/golang/go synced 2024-11-14 15:00:27 -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:
Rob Findley 2020-12-15 18:38:53 -05:00 committed by Robert Findley
parent f38da2cbb6
commit 09abd23d9e
2 changed files with 159 additions and 63 deletions

View File

@ -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{
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,8 +790,12 @@ 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) },
var binaryOpPredicates opPredicates
func init() {
// Setting binaryOpPredicates in init avoids declaration cycles.
binaryOpPredicates = opPredicates{
token.ADD: isNumericOrString,
token.SUB: isNumeric,
token.MUL: isNumeric,
token.QUO: isNumeric,
@ -799,6 +808,7 @@ var binaryOpPredicates = opPredicates{
token.LAND: isBoolean,
token.LOR: isBoolean,
}
}
// The binary expression e may be nil. It's passed in for better error messages only.

View File

@ -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.