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

View as plain text