github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/const.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file implements functions for untyped constant operands. 6 7 package types2 8 9 import ( 10 "go/constant" 11 "go/token" 12 "math" 13 14 "github.com/go-asm/go/cmd/compile/syntax" 15 . "github.com/go-asm/go/types/errors" 16 ) 17 18 // overflow checks that the constant x is representable by its type. 19 // For untyped constants, it checks that the value doesn't become 20 // arbitrarily large. 21 func (check *Checker) overflow(x *operand, opPos syntax.Pos) { 22 assert(x.mode == constant_) 23 24 if x.val.Kind() == constant.Unknown { 25 // TODO(gri) We should report exactly what went wrong. At the 26 // moment we don't have the (go/constant) API for that. 27 // See also TODO in go/constant/value.go. 28 check.error(atPos(opPos), InvalidConstVal, "constant result is not representable") 29 return 30 } 31 32 // Typed constants must be representable in 33 // their type after each constant operation. 34 // x.typ cannot be a type parameter (type 35 // parameters cannot be constant types). 36 if isTyped(x.typ) { 37 check.representable(x, under(x.typ).(*Basic)) 38 return 39 } 40 41 // Untyped integer values must not grow arbitrarily. 42 const prec = 512 // 512 is the constant precision 43 if x.val.Kind() == constant.Int && constant.BitLen(x.val) > prec { 44 op := opName(x.expr) 45 if op != "" { 46 op += " " 47 } 48 check.errorf(atPos(opPos), InvalidConstVal, "constant %soverflow", op) 49 x.val = constant.MakeUnknown() 50 } 51 } 52 53 // representableConst reports whether x can be represented as 54 // value of the given basic type and for the configuration 55 // provided (only needed for int/uint sizes). 56 // 57 // If rounded != nil, *rounded is set to the rounded value of x for 58 // representable floating-point and complex values, and to an Int 59 // value for integer values; it is left alone otherwise. 60 // It is ok to provide the addressof the first argument for rounded. 61 // 62 // The check parameter may be nil if representableConst is invoked 63 // (indirectly) through an exported API call (AssignableTo, ConvertibleTo) 64 // because we don't need the Checker's config for those calls. 65 func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *constant.Value) bool { 66 if x.Kind() == constant.Unknown { 67 return true // avoid follow-up errors 68 } 69 70 var conf *Config 71 if check != nil { 72 conf = check.conf 73 } 74 75 sizeof := func(T Type) int64 { 76 s := conf.sizeof(T) 77 return s 78 } 79 80 switch { 81 case isInteger(typ): 82 x := constant.ToInt(x) 83 if x.Kind() != constant.Int { 84 return false 85 } 86 if rounded != nil { 87 *rounded = x 88 } 89 if x, ok := constant.Int64Val(x); ok { 90 switch typ.kind { 91 case Int: 92 var s = uint(sizeof(typ)) * 8 93 return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 94 case Int8: 95 const s = 8 96 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 97 case Int16: 98 const s = 16 99 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 100 case Int32: 101 const s = 32 102 return -1<<(s-1) <= x && x <= 1<<(s-1)-1 103 case Int64, UntypedInt: 104 return true 105 case Uint, Uintptr: 106 if s := uint(sizeof(typ)) * 8; s < 64 { 107 return 0 <= x && x <= int64(1)<<s-1 108 } 109 return 0 <= x 110 case Uint8: 111 const s = 8 112 return 0 <= x && x <= 1<<s-1 113 case Uint16: 114 const s = 16 115 return 0 <= x && x <= 1<<s-1 116 case Uint32: 117 const s = 32 118 return 0 <= x && x <= 1<<s-1 119 case Uint64: 120 return 0 <= x 121 default: 122 unreachable() 123 } 124 } 125 // x does not fit into int64 126 switch n := constant.BitLen(x); typ.kind { 127 case Uint, Uintptr: 128 var s = uint(sizeof(typ)) * 8 129 return constant.Sign(x) >= 0 && n <= int(s) 130 case Uint64: 131 return constant.Sign(x) >= 0 && n <= 64 132 case UntypedInt: 133 return true 134 } 135 136 case isFloat(typ): 137 x := constant.ToFloat(x) 138 if x.Kind() != constant.Float { 139 return false 140 } 141 switch typ.kind { 142 case Float32: 143 if rounded == nil { 144 return fitsFloat32(x) 145 } 146 r := roundFloat32(x) 147 if r != nil { 148 *rounded = r 149 return true 150 } 151 case Float64: 152 if rounded == nil { 153 return fitsFloat64(x) 154 } 155 r := roundFloat64(x) 156 if r != nil { 157 *rounded = r 158 return true 159 } 160 case UntypedFloat: 161 return true 162 default: 163 unreachable() 164 } 165 166 case isComplex(typ): 167 x := constant.ToComplex(x) 168 if x.Kind() != constant.Complex { 169 return false 170 } 171 switch typ.kind { 172 case Complex64: 173 if rounded == nil { 174 return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x)) 175 } 176 re := roundFloat32(constant.Real(x)) 177 im := roundFloat32(constant.Imag(x)) 178 if re != nil && im != nil { 179 *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) 180 return true 181 } 182 case Complex128: 183 if rounded == nil { 184 return fitsFloat64(constant.Real(x)) && fitsFloat64(constant.Imag(x)) 185 } 186 re := roundFloat64(constant.Real(x)) 187 im := roundFloat64(constant.Imag(x)) 188 if re != nil && im != nil { 189 *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) 190 return true 191 } 192 case UntypedComplex: 193 return true 194 default: 195 unreachable() 196 } 197 198 case isString(typ): 199 return x.Kind() == constant.String 200 201 case isBoolean(typ): 202 return x.Kind() == constant.Bool 203 } 204 205 return false 206 } 207 208 func fitsFloat32(x constant.Value) bool { 209 f32, _ := constant.Float32Val(x) 210 f := float64(f32) 211 return !math.IsInf(f, 0) 212 } 213 214 func roundFloat32(x constant.Value) constant.Value { 215 f32, _ := constant.Float32Val(x) 216 f := float64(f32) 217 if !math.IsInf(f, 0) { 218 return constant.MakeFloat64(f) 219 } 220 return nil 221 } 222 223 func fitsFloat64(x constant.Value) bool { 224 f, _ := constant.Float64Val(x) 225 return !math.IsInf(f, 0) 226 } 227 228 func roundFloat64(x constant.Value) constant.Value { 229 f, _ := constant.Float64Val(x) 230 if !math.IsInf(f, 0) { 231 return constant.MakeFloat64(f) 232 } 233 return nil 234 } 235 236 // representable checks that a constant operand is representable in the given 237 // basic type. 238 func (check *Checker) representable(x *operand, typ *Basic) { 239 v, code := check.representation(x, typ) 240 if code != 0 { 241 check.invalidConversion(code, x, typ) 242 x.mode = invalid 243 return 244 } 245 assert(v != nil) 246 x.val = v 247 } 248 249 // representation returns the representation of the constant operand x as the 250 // basic type typ. 251 // 252 // If no such representation is possible, it returns a non-zero error code. 253 func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, Code) { 254 assert(x.mode == constant_) 255 v := x.val 256 if !representableConst(x.val, check, typ, &v) { 257 if isNumeric(x.typ) && isNumeric(typ) { 258 // numeric conversion : error msg 259 // 260 // integer -> integer : overflows 261 // integer -> float : overflows (actually not possible) 262 // float -> integer : truncated 263 // float -> float : overflows 264 // 265 if !isInteger(x.typ) && isInteger(typ) { 266 return nil, TruncatedFloat 267 } else { 268 return nil, NumericOverflow 269 } 270 } 271 return nil, InvalidConstVal 272 } 273 return v, 0 274 } 275 276 func (check *Checker) invalidConversion(code Code, x *operand, target Type) { 277 msg := "cannot convert %s to type %s" 278 switch code { 279 case TruncatedFloat: 280 msg = "%s truncated to %s" 281 case NumericOverflow: 282 msg = "%s overflows %s" 283 } 284 check.errorf(x, code, msg, x, target) 285 } 286 287 // convertUntyped attempts to set the type of an untyped value to the target type. 288 func (check *Checker) convertUntyped(x *operand, target Type) { 289 newType, val, code := check.implicitTypeAndValue(x, target) 290 if code != 0 { 291 t := target 292 if !isTypeParam(target) { 293 t = safeUnderlying(target) 294 } 295 check.invalidConversion(code, x, t) 296 x.mode = invalid 297 return 298 } 299 if val != nil { 300 x.val = val 301 check.updateExprVal(x.expr, val) 302 } 303 if newType != x.typ { 304 x.typ = newType 305 check.updateExprType(x.expr, newType, false) 306 } 307 }