summaryrefslogtreecommitdiff
path: root/src/go/types
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2023-05-08 14:42:19 -0700
committerGopher Robot <gobot@golang.org>2023-05-08 22:12:00 +0000
commit2532352ffba751726419a866e9ae5cb5529637fe (patch)
tree6f50dee9e815f70fa1cd1f8de918fdfa31c06a8f /src/go/types
parentf3247f6110d41793b0fc1c1a9d6541e649acd438 (diff)
downloadgo-git-2532352ffba751726419a866e9ae5cb5529637fe.tar.gz
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 <gri@google.com> Auto-Submit: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com> Run-TryBot: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/go/types')
-rw-r--r--src/go/types/const.go308
-rw-r--r--src/go/types/expr.go293
-rw-r--r--src/go/types/generate_test.go1
3 files changed, 309 insertions, 293 deletions
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)<<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(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
+}
+
+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)<<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(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
-}
-
-// 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,