From 2532352ffba751726419a866e9ae5cb5529637fe Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 8 May 2023 14:42:19 -0700 Subject: [PATCH] go/types, types2: move functions for untyped constants into const.go No changes to the moved functions. Generate const.go for go/types. Change-Id: I5ac412cecd9f618676a01138aed36428bbce3311 Reviewed-on: https://go-review.googlesource.com/c/go/+/493715 Reviewed-by: Robert Griesemer Auto-Submit: Robert Griesemer Reviewed-by: Robert Findley Run-TryBot: Robert Griesemer TryBot-Result: Gopher Robot --- src/cmd/compile/internal/types2/const.go | 307 ++++++++++++++++++++++ src/cmd/compile/internal/types2/expr.go | 293 --------------------- src/go/types/const.go | 308 +++++++++++++++++++++++ src/go/types/expr.go | 293 --------------------- src/go/types/generate_test.go | 1 + 5 files changed, 616 insertions(+), 586 deletions(-) create mode 100644 src/cmd/compile/internal/types2/const.go create mode 100644 src/go/types/const.go diff --git a/src/cmd/compile/internal/types2/const.go b/src/cmd/compile/internal/types2/const.go new file mode 100644 index 0000000000..9be578c647 --- /dev/null +++ b/src/cmd/compile/internal/types2/const.go @@ -0,0 +1,307 @@ +// 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 types2 + +import ( + "cmd/compile/internal/syntax" + "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 syntax.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) + assert(s == 4 || s == 8) + 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)<= 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 +} + +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) + } +} diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 4dc46d4a48..0f473293bc 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -12,7 +12,6 @@ import ( "go/constant" "go/token" . "internal/types/errors" - "math" ) /* @@ -84,41 +83,6 @@ func (check *Checker) op(m opPredicates, x *operand, op syntax.Operator) bool { return true } -// 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, pos syntax.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(pos, 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(pos, InvalidConstVal, "constant %soverflow", op) - x.val = constant.MakeUnknown() - } -} - // opPos returns the position of the operator if x is an operation; // otherwise it returns the start position of x. func opPos(x syntax.Expr) syntax.Pos { @@ -263,241 +227,6 @@ func isComparison(op syntax.Operator) 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 -} - -// 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) - assert(s == 4 || s == 8) - 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)<= 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 -} - -// 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) -} - // updateExprType updates the type of x to typ and invokes itself // recursively for the operands of x, depending on expression kind. // If typ is still an untyped and not the final type, updateExprType @@ -656,28 +385,6 @@ func (check *Checker) updateExprVal(x syntax.Expr, val constant.Value) { } } -// 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) - } -} - // implicitTypeAndValue returns the implicit type of x when used in a context // where the target type is expected. If no such implicit conversion is // possible, it returns a nil Type and non-zero error code. diff --git a/src/go/types/const.go b/src/go/types/const.go new file mode 100644 index 0000000000..66fa60804f --- /dev/null +++ b/src/go/types/const.go @@ -0,0 +1,308 @@ +// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT. + +// 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) + assert(s == 4 || s == 8) + 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)<= 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 +} + +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) + } +} diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 59f0b7481a..b0e1422b01 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -13,7 +13,6 @@ import ( "go/internal/typeparams" "go/token" . "internal/types/errors" - "math" ) /* @@ -85,41 +84,6 @@ func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool { return true } -// 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() - } -} - // opName returns the name of the operation if x is an operation // that might overflow; otherwise it returns the empty string. func opName(e ast.Expr) string { @@ -251,241 +215,6 @@ func isComparison(op token.Token) 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 -} - -// 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) - assert(s == 4 || s == 8) - 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)<= 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 -} - -// 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) -} - // updateExprType updates the type of x to typ and invokes itself // recursively for the operands of x, depending on expression kind. // If typ is still an untyped and not the final type, updateExprType @@ -620,28 +349,6 @@ func (check *Checker) updateExprVal(x ast.Expr, val constant.Value) { } } -// 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) - } -} - // implicitTypeAndValue returns the implicit type of x when used in a context // where the target type is expected. If no such implicit conversion is // possible, it returns a nil Type and non-zero error code. diff --git a/src/go/types/generate_test.go b/src/go/types/generate_test.go index 939fdb38f4..c5e114aaec 100644 --- a/src/go/types/generate_test.go +++ b/src/go/types/generate_test.go @@ -98,6 +98,7 @@ var filemap = map[string]action{ "array.go": nil, "basic.go": nil, "chan.go": nil, + "const.go": func(f *ast.File) { fixTokenPos(f) }, "context.go": nil, "context_test.go": nil, "gccgosizes.go": nil, -- 2.48.1