func (check *Checker) arrayLength(e ast.Expr) int64 { var x operand check.expr(&x, e) if x.mode != constant_ { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } return 0 } if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { if representableConst(val, check.conf, Typ[Int], nil) { if n, ok := constant.Int64Val(val); ok && n >= 0 { return n } check.errorf(x.pos(), "invalid array length %s", &x) return 0 } } } check.errorf(x.pos(), "array length %s must be integer", &x) return 0 }
// index checks an index expression for validity. // If max >= 0, it is the upper bound for index. // If index is valid and the result i >= 0, then i is the constant value of index. func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) { var x operand check.expr(&x, index) if x.mode == invalid { return } // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "index %s must be integer", &x) return } // a constant index i must be in bounds if x.mode == constant_ { if constant.Sign(x.val) < 0 { check.invalidArg(x.pos(), "index %s must not be negative", &x) return } i, valid = constant.Int64Val(constant.ToInt(x.val)) if !valid || max >= 0 && i >= max { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < max ] return i, true } return -1, true }
func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) { untypedx := isUntyped(x.typ) var xval constant.Value if x.mode == constant_ { xval = constant.ToInt(x.val) } if isInteger(x.typ) || untypedx && xval != nil && xval.Kind() == constant.Int { // The lhs is of integer type or an untyped constant representable // as an integer. Nothing to do. } else { // shift has no chance check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } // spec: "The right operand in a shift expression must have unsigned // integer type or be an untyped constant that can be converted to // unsigned integer type." switch { case isUnsigned(y.typ): // nothing to do case isUntyped(y.typ): check.convertUntyped(y, Typ[UntypedInt]) if y.mode == invalid { x.mode = invalid return } default: check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } if x.mode == constant_ { if y.mode == constant_ { // rhs must be an integer value yval := constant.ToInt(y.val) if yval.Kind() != constant.Int { check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } // rhs must be within reasonable bounds const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64 s, ok := constant.Uint64Val(yval) if !ok || s > shiftBound { check.invalidOp(y.pos(), "invalid shift count %s", y) x.mode = invalid return } // The lhs is representable as an integer but may not be an integer // (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) { x.typ = Typ[UntypedInt] } // x is a constant so xval != nil and it must be of Int kind. x.val = constant.Shift(xval, op, uint(s)) // Typed constants must be representable in // their type after each constant operation. if isTyped(x.typ) { if e != nil { x.expr = e // for better error message } check.representable(x, x.typ.Underlying().(*Basic)) } return } // non-constant shift with constant lhs if untypedx { // spec: "If the left operand of a non-constant shift // expression is an untyped constant, the type of the // constant is what it would be if the shift expression // were replaced by its left operand alone.". // // Delay operand checking until we know the final type // by marking the lhs expression as lhs shift operand. // // Usually (in correct programs), the lhs expression // is in the untyped map. However, it is possible to // create incorrect programs where the same expression // is evaluated twice (via a declaration cycle) such // that the lhs expression type is determined in the // first round and thus deleted from the map, and then // not found in the second round (double insertion of // the same expr node still just leads to one entry for // that node, and it can only be deleted once). // Be cautious and check for presence of entry. // Example: var e, f = int(1<<""[f]) // issue 11347 if info, found := check.untyped[x.expr]; found { info.isLhs = true check.untyped[x.expr] = info } // keep x's type x.mode = value return } } // constant rhs must be >= 0 if y.mode == constant_ && constant.Sign(y.val) < 0 { check.invalidOp(y.pos(), "shift count %s must not be negative", y) } // non-constant shift - lhs must be an integer if !isInteger(x.typ) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } x.mode = value }
// representableConst reports whether x can be represented as // value of the given basic type and for the configuration // provided (only needed for int/uint sizes). // // If rounded != nil, *rounded is set to the rounded value of x for // representable floating-point and complex values, and to an Int // value for integer values; it is left alone otherwise. // It is ok to provide the addressof the first argument for rounded. func representableConst(x constant.Value, conf *Config, typ *Basic, rounded *constant.Value) bool { if x.Kind() == constant.Unknown { return true // avoid follow-up errors } switch { case isInteger(typ): x := constant.ToInt(x) if x.Kind() != constant.Int { return false } if rounded != nil { *rounded = x } if x, ok := constant.Int64Val(x); ok { switch typ.kind { case Int: var s = uint(conf.sizeof(typ)) * 8 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 case Int8: const s = 8 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int16: const s = 16 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int32: const s = 32 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 case Int64, UntypedInt: return true case Uint, Uintptr: if s := uint(conf.sizeof(typ)) * 8; s < 64 { return 0 <= x && x <= int64(1)<<s-1 } return 0 <= x case Uint8: const s = 8 return 0 <= x && x <= 1<<s-1 case Uint16: const s = 16 return 0 <= x && x <= 1<<s-1 case Uint32: const s = 32 return 0 <= x && x <= 1<<s-1 case Uint64: return 0 <= x default: unreachable() } } // x does not fit into int64 switch n := constant.BitLen(x); typ.kind { case Uint, Uintptr: var s = uint(conf.sizeof(typ)) * 8 return constant.Sign(x) >= 0 && n <= int(s) case Uint64: return constant.Sign(x) >= 0 && n <= 64 case UntypedInt: return true } case isFloat(typ): x := constant.ToFloat(x) if x.Kind() != constant.Float { return false } switch typ.kind { case Float32: if rounded == nil { return fitsFloat32(x) } r := roundFloat32(x) if r != nil { *rounded = r return true } case Float64: if rounded == nil { return fitsFloat64(x) } r := roundFloat64(x) if r != nil { *rounded = r return true } case UntypedFloat: return true default: unreachable() } case isComplex(typ): x := constant.ToComplex(x) if x.Kind() != constant.Complex { return false } switch typ.kind { case Complex64: if rounded == nil { return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x)) } re := roundFloat32(constant.Real(x)) im := roundFloat32(constant.Imag(x)) if re != nil && im != nil { *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) return true } case Complex128: if rounded == nil { return fitsFloat64(constant.Real(x)) && fitsFloat64(constant.Imag(x)) } re := roundFloat64(constant.Real(x)) im := roundFloat64(constant.Imag(x)) if re != nil && im != nil { *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) return true } case UntypedComplex: return true default: unreachable() } case isString(typ): return x.Kind() == constant.String case isBoolean(typ): return x.Kind() == constant.Bool } return false }