cmd/compile/internal/types2: rename isX predicates to allX, add simple is_X (step 1 of 2)

Rename the isX predicates to allX to clearly identify that these
predicates are looking inside type parameters.

Introduce is_X as predicates that do not look
inside type parameters so we can see all call sites.
The next CL will rename them all back to isX.

Review all call sites and use correct predicate.

Replace the single helper function is with isBasic and allBasic.

Change-Id: I3430ccfc466fdedf4b58a6158f95d47b9020f7a5

Change-Id: I81116b87cf8f2e17526723c7440676d133057aca
Reviewed-on: https://go-review.googlesource.com/c/go/+/360955
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-11-02 17:31:51 -07:00
parent 747e4afe07
commit 9e4e23c724
9 changed files with 114 additions and 94 deletions

View File

@ -101,7 +101,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
if x.mode == invalid {
return
}
if isString(x.typ) {
if allString(x.typ) {
if check.Types != nil {
sig := makeSig(S, S, x.typ)
sig.variadic = true
@ -146,7 +146,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
var val constant.Value
switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if is_String(t) && id == _Len {
if x.mode == constant_ {
mode = constant_
val = constant.MakeInt64(int64(len(constant.StringVal(x.val))))
@ -182,7 +182,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
if t.underIs(func(t Type) bool {
switch t := arrayPtrDeref(t).(type) {
case *Basic:
if isString(t) && id == _Len {
if is_String(t) && id == _Len {
return true
}
case *Array, *Slice, *Chan:
@ -267,7 +267,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// because shifts of floats are not permitted)
if x.mode == constant_ && y.mode == constant_ {
toFloat := func(x *operand) {
if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 {
if is_Numeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 {
x.typ = Typ[UntypedFloat]
}
}
@ -398,7 +398,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
if x.mode == constant_ {
// an untyped constant number can always be considered
// as a complex constant
if isNumeric(x.typ) {
if is_Numeric(x.typ) {
x.typ = Typ[UntypedComplex]
}
} else {
@ -726,7 +726,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// assert(pred) causes a typechecker error if pred is false.
// The result of assert is the value of pred if there is no error.
// Note: assert is only available in self-test mode.
if x.mode != constant_ || !isBoolean(x.typ) {
if x.mode != constant_ || !is_Boolean(x.typ) {
check.errorf(x, invalidArg+"%s is not a boolean constant", x)
return
}
@ -802,7 +802,7 @@ func structure(typ Type) Type {
func structureString(typ Type) Type {
var su Type
if underIs(typ, func(u Type) bool {
if isString(u) {
if is_String(u) {
u = NewSlice(universeByte)
}
if su != nil && !Identical(su, u) {

View File

@ -430,9 +430,9 @@ func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Ty
}
if mode == constant_ {
assert(val != nil)
// We check is(typ, IsConstType) here as constant expressions may be
// We check allBasic(typ, IsConstType) here as constant expressions may be
// recorded as type parameters.
assert(typ == Typ[Invalid] || is(typ, IsConstType))
assert(typ == Typ[Invalid] || allBasic(typ, IsConstType))
}
if m := check.Types; m != nil {
m[x] = TypeAndValue{mode, typ, val}
@ -462,7 +462,7 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
if a[0] == nil || a[1] == nil {
return
}
assert(isTyped(a[0]) && isTyped(a[1]) && (isBoolean(a[1]) || a[1] == universeError))
assert(isTyped(a[0]) && isTyped(a[1]) && (is_Boolean(a[1]) || a[1] == universeError))
if m := check.Types; m != nil {
for {
tv := m[x]

View File

@ -22,7 +22,7 @@ func (check *Checker) conversion(x *operand, T Type) {
// nothing to do
case representableConst(x.val, check, t, val):
return true
case isInteger(x.typ) && isString(t):
case is_Integer(x.typ) && is_String(t):
codepoint := unicode.ReplacementChar
if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
codepoint = rune(i)
@ -93,7 +93,7 @@ func (check *Checker) conversion(x *operand, T Type) {
// ok
} else if IsInterface(T) || constArg && !isConstType(T) {
final = Default(x.typ)
} else if isInteger(x.typ) && isString(T) {
} else if is_Integer(x.typ) && allString(T) {
final = x.typ
}
check.updateExprType(x.expr, final, true)
@ -197,22 +197,22 @@ func convertibleToImpl(check *Checker, V, T Type, cause *string) bool {
}
// "V and T are both integer or floating point types"
if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
if is_IntegerOrFloat(V) && is_IntegerOrFloat(T) {
return true
}
// "V and T are both complex types"
if isComplex(V) && isComplex(T) {
if is_Complex(V) && is_Complex(T) {
return true
}
// "V is an integer or a slice of bytes or runes and T is a string type"
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
if (is_Integer(V) || isBytesOrRunes(Vu)) && is_String(T) {
return true
}
// "V is a string and T is a slice of bytes or runes"
if isString(V) && isBytesOrRunes(Tu) {
if is_String(V) && isBytesOrRunes(Tu) {
return true
}

View File

@ -63,10 +63,10 @@ var unaryOpPredicates opPredicates
func init() {
// Setting unaryOpPredicates in init avoids declaration cycles.
unaryOpPredicates = opPredicates{
syntax.Add: isNumeric,
syntax.Sub: isNumeric,
syntax.Xor: isInteger,
syntax.Not: isBoolean,
syntax.Add: allNumeric,
syntax.Sub: allNumeric,
syntax.Xor: allInteger,
syntax.Not: allBoolean,
}
}
@ -225,7 +225,7 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
return
}
var prec uint
if isUnsigned(x.typ) {
if is_Unsigned(x.typ) {
prec = uint(check.conf.sizeof(x.typ) * 8)
}
x.val = constant.UnaryOp(op2tok[e.Op], x.val, prec)
@ -302,7 +302,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
}
switch {
case isInteger(typ):
case is_Integer(typ):
x := constant.ToInt(x)
if x.Kind() != constant.Int {
return false
@ -357,7 +357,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
return true
}
case isFloat(typ):
case is_Float(typ):
x := constant.ToFloat(x)
if x.Kind() != constant.Float {
return false
@ -387,7 +387,7 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
unreachable()
}
case isComplex(typ):
case is_Complex(typ):
x := constant.ToComplex(x)
if x.Kind() != constant.Complex {
return false
@ -419,10 +419,10 @@ func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *c
unreachable()
}
case isString(typ):
case is_String(typ):
return x.Kind() == constant.String
case isBoolean(typ):
case is_Boolean(typ):
return x.Kind() == constant.Bool
}
@ -474,7 +474,7 @@ func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, er
assert(x.mode == constant_)
v := x.val
if !representableConst(x.val, check, typ, &v) {
if isNumeric(x.typ) && isNumeric(typ) {
if is_Numeric(x.typ) && is_Numeric(typ) {
// numeric conversion : error msg
//
// integer -> integer : overflows
@ -482,7 +482,7 @@ func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, er
// float -> integer : truncated
// float -> float : overflows
//
if !isInteger(x.typ) && isInteger(typ) {
if !is_Integer(x.typ) && is_Integer(typ) {
return nil, _TruncatedFloat
} else {
return nil, _NumericOverflow
@ -630,7 +630,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) {
// If x is the lhs of a shift, its final type must be integer.
// We already know from the shift check that it is representable
// as an integer if it is a constant.
if !isInteger(typ) {
if !is_Integer(typ) {
check.errorf(x, invalidOp+"shifted operand %s (type %s) must be integer", x, typ)
return
}
@ -692,7 +692,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
// both x and target are untyped
xkind := x.typ.(*Basic).kind
tkind := target.(*Basic).kind
if isNumeric(x.typ) && isNumeric(target) {
if is_Numeric(x.typ) && is_Numeric(target) {
if xkind < tkind {
return target, nil, 0
}
@ -710,10 +710,10 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return nil, nil, _InvalidUntypedConversion
}
switch t := under(target).(type) {
switch u := under(target).(type) {
case *Basic:
if x.mode == constant_ {
v, code := check.representation(x, t)
v, code := check.representation(x, u)
if code != 0 {
return nil, nil, code
}
@ -725,18 +725,18 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
// the value nil.
switch x.typ.(*Basic).kind {
case UntypedBool:
if !isBoolean(target) {
if !is_Boolean(target) {
return nil, nil, _InvalidUntypedConversion
}
case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
if !isNumeric(target) {
if !is_Numeric(target) {
return nil, nil, _InvalidUntypedConversion
}
case UntypedString:
// Non-constant untyped string values are not permitted by the spec and
// should not occur during normal typechecking passes, but this path is
// reachable via the AssignableTo API.
if !isString(target) {
if !is_String(target) {
return nil, nil, _InvalidUntypedConversion
}
default:
@ -744,7 +744,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
}
case *TypeParam:
// TODO(gri) review this code - doesn't look quite right
ok := t.underIs(func(t Type) bool {
ok := u.underIs(func(t Type) bool {
target, _, _ := check.implicitTypeAndValue(x, t)
return target != nil
})
@ -755,7 +755,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
// Update operand types to the default type rather than the target
// (interface) type: values must have concrete dynamic types.
// Untyped nil was handled upfront.
if !t.Empty() {
if !u.Empty() {
return nil, nil, _InvalidUntypedConversion // cannot assign untyped values to non-empty interfaces
}
return Default(x.typ), nil, 0 // default type for nil is nil
@ -779,7 +779,7 @@ func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
defined = isOrdered(x.typ) && isOrdered(y.typ)
defined = allOrdered(x.typ) && allOrdered(y.typ)
default:
unreachable()
}
@ -833,7 +833,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
xval = constant.ToInt(x.val)
}
if isInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
if allInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
// The lhs is of integer type or an untyped constant representable
// as an integer. Nothing to do.
} else {
@ -856,7 +856,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
}
}
// Caution: Check for isUntyped first because isInteger includes untyped
// Caution: Check for isUntyped first because is_Integer includes untyped
// integers (was bug #43697).
if isUntyped(y.typ) {
check.convertUntyped(y, Typ[Uint])
@ -864,11 +864,11 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
x.mode = invalid
return
}
} else if !isInteger(y.typ) {
} else if !allInteger(y.typ) {
check.errorf(y, invalidOp+"shift count %s must be integer", y)
x.mode = invalid
return
} else if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
} else if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
check.errorf(y, invalidOp+"signed shift count %s requires go1.13 or later", y)
x.mode = invalid
return
@ -880,7 +880,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
if x.val.Kind() == constant.Unknown || y.val.Kind() == constant.Unknown {
x.val = constant.MakeUnknown()
// ensure the correct type - see comment below
if !isInteger(x.typ) {
if !is_Integer(x.typ) {
x.typ = Typ[UntypedInt]
}
return
@ -897,7 +897,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
// (e.g., 2.0, an untyped float) - this can only happen for untyped
// non-integer numeric constants. Correct the type so that the shift
// result is of integer type.
if !isInteger(x.typ) {
if !is_Integer(x.typ) {
x.typ = Typ[UntypedInt]
}
// x is a constant so xval != nil and it must be of Int kind.
@ -939,7 +939,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
}
// non-constant shift - lhs must be an integer
if !isInteger(x.typ) {
if !allInteger(x.typ) {
check.errorf(x, invalidOp+"shifted operand %s must be integer", x)
x.mode = invalid
return
@ -953,19 +953,19 @@ var binaryOpPredicates opPredicates
func init() {
// Setting binaryOpPredicates in init avoids declaration cycles.
binaryOpPredicates = opPredicates{
syntax.Add: isNumericOrString,
syntax.Sub: isNumeric,
syntax.Mul: isNumeric,
syntax.Div: isNumeric,
syntax.Rem: isInteger,
syntax.Add: allNumericOrString,
syntax.Sub: allNumeric,
syntax.Mul: allNumeric,
syntax.Div: allNumeric,
syntax.Rem: allInteger,
syntax.And: isInteger,
syntax.Or: isInteger,
syntax.Xor: isInteger,
syntax.AndNot: isInteger,
syntax.And: allInteger,
syntax.Or: allInteger,
syntax.Xor: allInteger,
syntax.AndNot: allInteger,
syntax.AndAnd: isBoolean,
syntax.OrOr: isBoolean,
syntax.AndAnd: allBoolean,
syntax.OrOr: allBoolean,
}
}
@ -995,10 +995,10 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
if IsInterface(x.typ) || IsInterface(y.typ) {
return true
}
if isBoolean(x.typ) != isBoolean(y.typ) {
if allBoolean(x.typ) != allBoolean(y.typ) {
return false
}
if isString(x.typ) != isString(y.typ) {
if allString(x.typ) != allString(y.typ) {
return false
}
if x.isNil() && !hasNil(y.typ) {
@ -1047,14 +1047,14 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
if op == syntax.Div || op == syntax.Rem {
// check for zero divisor
if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
if (x.mode == constant_ || allInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
check.error(&y, invalidOp+"division by zero")
x.mode = invalid
return
}
// check for divisor underflow in complex division (see issue 20227)
if x.mode == constant_ && y.mode == constant_ && isComplex(x.typ) {
if x.mode == constant_ && y.mode == constant_ && is_Complex(x.typ) {
re, im := constant.Real(y.val), constant.Imag(y.val)
re2, im2 := constant.BinaryOp(re, token.MUL, re), constant.BinaryOp(im, token.MUL, im)
if constant.Sign(re2) == 0 && constant.Sign(im2) == 0 {
@ -1074,7 +1074,7 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
}
// force integer division for integer operands
tok := op2tok[op]
if op == syntax.Div && isInteger(x.typ) {
if op == syntax.Div && is_Integer(x.typ) {
tok = token.QUO_ASSIGN
}
x.val = constant.BinaryOp(x.val, tok, y.val)

View File

@ -51,7 +51,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
length := int64(-1) // valid if >= 0
switch typ := under(x.typ).(type) {
case *Basic:
if isString(typ) {
if is_String(typ) {
valid = true
if x.mode == constant_ {
length = int64(len(constant.StringVal(x.val)))
@ -109,7 +109,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
var k, e Type // k is only set for maps
switch t := u.(type) {
case *Basic:
if isString(t) {
if is_String(t) {
e = universeByte
mode = value
}
@ -217,7 +217,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
return
case *Basic:
if isString(u) {
if is_String(u) {
if e.Full {
check.error(x, invalidOp+"3-index slice of string")
x.mode = invalid
@ -386,7 +386,7 @@ func (check *Checker) isValidIndex(x *operand, what string, allowNegative bool)
}
// spec: "the index x must be of integer type or an untyped constant"
if !isInteger(x.typ) {
if !allInteger(x.typ) {
check.errorf(x, invalidArg+"%s %s must be integer", what, x)
return false
}

View File

@ -25,33 +25,55 @@ func isGeneric(typ Type) bool {
return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
}
func is(typ Type, what BasicInfo) bool {
switch t := under(typ).(type) {
// The is_X predicates below report whether t is an X.
// If t is a type parameter the result is false; i.e.,
// these predicates don't look inside a type parameter.
func is_Boolean(t Type) bool { return isBasic(t, IsBoolean) }
func is_Integer(t Type) bool { return isBasic(t, IsInteger) }
func is_Unsigned(t Type) bool { return isBasic(t, IsUnsigned) }
func is_Float(t Type) bool { return isBasic(t, IsFloat) }
func is_Complex(t Type) bool { return isBasic(t, IsComplex) }
func is_Numeric(t Type) bool { return isBasic(t, IsNumeric) }
func is_String(t Type) bool { return isBasic(t, IsString) }
func is_IntegerOrFloat(t Type) bool { return isBasic(t, IsInteger|IsFloat) }
// isBasic reports whether under(t) is a basic type with the specified info.
// If t is a type parameter the result is false; i.e.,
// isBasic does not look inside a type parameter.
func isBasic(t Type, info BasicInfo) bool {
u, _ := under(t).(*Basic)
return u != nil && u.info&info != 0
}
// The allX predicates below report whether t is an X.
// If t is a type parameter the result is true if is_X is true
// for all specified types of the type parameter's type set.
// allX is an optimized version of is_X(structure(t)) (which
// is the same as underIs(t, is_X)).
func allBoolean(t Type) bool { return allBasic(t, IsBoolean) }
func allInteger(t Type) bool { return allBasic(t, IsInteger) }
func allUnsigned(t Type) bool { return allBasic(t, IsUnsigned) }
func allNumeric(t Type) bool { return allBasic(t, IsNumeric) }
func allString(t Type) bool { return allBasic(t, IsString) }
func allOrdered(t Type) bool { return allBasic(t, IsOrdered) }
func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
// allBasic reports whether under(t) is a basic type with the specified info.
// If t is a type parameter, the result is true if isBasic(t, info) is true
// for all specific types of the type parameter's type set.
// allBasic(t, info) is an optimized version of isBasic(structure(t), info).
func allBasic(t Type, info BasicInfo) bool {
switch u := under(t).(type) {
case *Basic:
return t.info&what != 0
return u.info&info != 0
case *TypeParam:
return t.underIs(func(t Type) bool { return is(t, what) })
return u.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
}
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 set 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.
@ -67,8 +89,6 @@ 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 := asBasic(typ)

View File

@ -82,7 +82,7 @@ func (s *StdSizes) Alignof(T Type) int64 {
return 1
}
// complex{64,128} are aligned like [2]float{32,64}.
if isComplex(T) {
if is_Complex(T) {
a /= 2
}
if a > s.MaxAlign {

View File

@ -443,7 +443,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
if x.mode == invalid {
return
}
if !isNumeric(x.typ) {
if !allNumeric(x.typ) {
check.errorf(lhs[0], invalidOp+"%s%s%s (non-numeric type %s)", lhs[0], s.Op, s.Op, x.typ)
return
}
@ -556,7 +556,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
check.simpleStmt(s.Init)
var x operand
check.expr(&x, s.Cond)
if x.mode != invalid && !isBoolean(x.typ) {
if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, "non-boolean condition in if statement")
}
check.stmt(inner, s.Then)
@ -645,7 +645,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
if s.Cond != nil {
var x operand
check.expr(&x, s.Cond)
if x.mode != invalid && !isBoolean(x.typ) {
if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, "non-boolean condition in for statement")
}
}
@ -942,7 +942,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
func rangeKeyVal(typ Type) (key, val Type) {
switch typ := arrayPtrDeref(typ).(type) {
case *Basic:
if isString(typ) {
if is_String(typ) {
return Typ[Int], universeRune // use 'rune' name
}
case *Array:

View File

@ -513,7 +513,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 {
return -1
}
if isUntyped(x.typ) || isInteger(x.typ) {
if isUntyped(x.typ) || is_Integer(x.typ) {
if val := constant.ToInt(x.val); val.Kind() == constant.Int {
if representableConst(val, check, Typ[Int], nil) {
if n, ok := constant.Int64Val(val); ok && n >= 0 {