// Copyright 2012 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 typechecking of conversions. package types import ( "go/constant" . "internal/types/errors" "unicode" ) // conversion type-checks the conversion T(x). // The result is in x. func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant_ constConvertibleTo := func(T Type, val *constant.Value) bool { switch t, _ := under(T).(*Basic); { case t == nil: // nothing to do case representableConst(x.val, check, t, val): return true case isInteger(x.typ) && isString(t): codepoint := unicode.ReplacementChar if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune { codepoint = rune(i) } if val != nil { *val = constant.MakeString(string(codepoint)) } return true } return false } var ok bool var cause string switch { case constArg && isConstType(T): // constant conversion ok = constConvertibleTo(T, &x.val) // A conversion from an integer constant to an integer type // can only fail if there's overflow. Give a concise error. // (go.dev/issue/63563) if !ok && isInteger(x.typ) && isInteger(T) { check.errorf(x, InvalidConversion, "constant %s overflows %s", x.val, T) x.mode = invalid return } case constArg && isTypeParam(T): // x is convertible to T if it is convertible // to each specific type in the type set of T. // If T's type set is empty, or if it doesn't // have specific types, constant x cannot be // converted. ok = T.(*TypeParam).underIs(func(u Type) bool { // u is nil if there are no specific type terms if u == nil { cause = check.sprintf("%s does not contain specific types", T) return false } if isString(x.typ) && isBytesOrRunes(u) { return true } if !constConvertibleTo(u, nil) { if isInteger(x.typ) && isInteger(u) { // see comment above on constant conversion cause = check.sprintf("constant %s overflows %s (in %s)", x.val, u, T) } else { cause = check.sprintf("cannot convert %s to type %s (in %s)", x, u, T) } return false } return true }) x.mode = value // type parameters are not constants case x.convertibleTo(check, T, &cause): // non-constant conversion ok = true x.mode = value } if !ok { if cause != "" { check.errorf(x, InvalidConversion, "cannot convert %s to type %s: %s", x, T, cause) } else { check.errorf(x, InvalidConversion, "cannot convert %s to type %s", x, T) } x.mode = invalid return } // The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...". if isUntyped(x.typ) { final := T // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. // - For constant integer to string conversions, keep the argument type. // (See also the TODO below.) if isNonTypeParamInterface(T) || constArg && !isConstType(T) || x.isNil() { final = Default(x.typ) // default type of untyped nil is untyped nil } else if x.mode == constant_ && isInteger(x.typ) && allString(T) { final = x.typ } check.updateExprType(x.expr, final, true) } x.typ = T } // TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type // of x is fully known, but that's not the case for say string(1<