| // Code generated by "go test -run=Generate -write=all"; DO NOT EDIT. |
| // Source: ../../cmd/compile/internal/types2/const.go |
| |
| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This file implements functions for untyped constant operands. |
| |
| package types |
| |
| import ( |
| "go/constant" |
| "go/token" |
| . "internal/types/errors" |
| "math" |
| ) |
| |
| // overflow checks that the constant x is representable by its type. |
| // For untyped constants, it checks that the value doesn't become |
| // arbitrarily large. |
| func (check *Checker) overflow(x *operand, opPos token.Pos) { |
| assert(x.mode == constant_) |
| |
| if x.val.Kind() == constant.Unknown { |
| // TODO(gri) We should report exactly what went wrong. At the |
| // moment we don't have the (go/constant) API for that. |
| // See also TODO in go/constant/value.go. |
| check.error(atPos(opPos), InvalidConstVal, "constant result is not representable") |
| return |
| } |
| |
| // Typed constants must be representable in |
| // their type after each constant operation. |
| // x.typ cannot be a type parameter (type |
| // parameters cannot be constant types). |
| if isTyped(x.typ) { |
| check.representable(x, under(x.typ).(*Basic)) |
| return |
| } |
| |
| // Untyped integer values must not grow arbitrarily. |
| const prec = 512 // 512 is the constant precision |
| if x.val.Kind() == constant.Int && constant.BitLen(x.val) > prec { |
| op := opName(x.expr) |
| if op != "" { |
| op += " " |
| } |
| check.errorf(atPos(opPos), InvalidConstVal, "constant %soverflow", op) |
| x.val = constant.MakeUnknown() |
| } |
| } |
| |
| // 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. |
| // |
| // The check parameter may be nil if representableConst is invoked |
| // (indirectly) through an exported API call (AssignableTo, ConvertibleTo) |
| // because we don't need the Checker's config for those calls. |
| func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *constant.Value) bool { |
| if x.Kind() == constant.Unknown { |
| return true // avoid follow-up errors |
| } |
| |
| var conf *Config |
| if check != nil { |
| conf = check.conf |
| } |
| |
| sizeof := func(T Type) int64 { |
| s := conf.sizeof(T) |
| return s |
| } |
| |
| 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(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(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: |
| panic("unreachable") |
| } |
| } |
| // x does not fit into int64 |
| switch n := constant.BitLen(x); typ.kind { |
| case Uint, Uintptr: |
| var s = uint(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: |
| panic("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: |
| panic("unreachable") |
| } |
| |
| case isString(typ): |
| return x.Kind() == constant.String |
| |
| case isBoolean(typ): |
| return x.Kind() == constant.Bool |
| } |
| |
| return false |
| } |
| |
| func fitsFloat32(x constant.Value) bool { |
| f32, _ := constant.Float32Val(x) |
| f := float64(f32) |
| return !math.IsInf(f, 0) |
| } |
| |
| func roundFloat32(x constant.Value) constant.Value { |
| f32, _ := constant.Float32Val(x) |
| f := float64(f32) |
| if !math.IsInf(f, 0) { |
| return constant.MakeFloat64(f) |
| } |
| return nil |
| } |
| |
| func fitsFloat64(x constant.Value) bool { |
| f, _ := constant.Float64Val(x) |
| return !math.IsInf(f, 0) |
| } |
| |
| func roundFloat64(x constant.Value) constant.Value { |
| f, _ := constant.Float64Val(x) |
| if !math.IsInf(f, 0) { |
| return constant.MakeFloat64(f) |
| } |
| return nil |
| } |
| |
| // representable checks that a constant operand is representable in the given |
| // basic type. |
| func (check *Checker) representable(x *operand, typ *Basic) { |
| v, code := check.representation(x, typ) |
| if code != 0 { |
| check.invalidConversion(code, x, typ) |
| x.mode = invalid |
| return |
| } |
| assert(v != nil) |
| x.val = v |
| } |
| |
| // representation returns the representation of the constant operand x as the |
| // basic type typ. |
| // |
| // If no such representation is possible, it returns a non-zero error code. |
| func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, Code) { |
| assert(x.mode == constant_) |
| v := x.val |
| if !representableConst(x.val, check, typ, &v) { |
| if isNumeric(x.typ) && isNumeric(typ) { |
| // numeric conversion : error msg |
| // |
| // integer -> integer : overflows |
| // integer -> float : overflows (actually not possible) |
| // float -> integer : truncated |
| // float -> float : overflows |
| // |
| if !isInteger(x.typ) && isInteger(typ) { |
| return nil, TruncatedFloat |
| } else { |
| return nil, NumericOverflow |
| } |
| } |
| return nil, InvalidConstVal |
| } |
| return v, 0 |
| } |
| |
| func (check *Checker) invalidConversion(code Code, x *operand, target Type) { |
| msg := "cannot convert %s to type %s" |
| switch code { |
| case TruncatedFloat: |
| msg = "%s truncated to %s" |
| case NumericOverflow: |
| msg = "%s overflows %s" |
| } |
| check.errorf(x, code, msg, x, target) |
| } |
| |
| // convertUntyped attempts to set the type of an untyped value to the target type. |
| func (check *Checker) convertUntyped(x *operand, target Type) { |
| newType, val, code := check.implicitTypeAndValue(x, target) |
| if code != 0 { |
| t := target |
| if !isTypeParam(target) { |
| t = safeUnderlying(target) |
| } |
| check.invalidConversion(code, x, t) |
| x.mode = invalid |
| return |
| } |
| if val != nil { |
| x.val = val |
| check.updateExprVal(x.expr, val) |
| } |
| if newType != x.typ { |
| x.typ = newType |
| check.updateExprType(x.expr, newType, false) |
| } |
| } |