Source file src/cmd/compile/internal/types2/const.go

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

View as plain text